@rex0220/kintone-sql-tools 3.1.0 → 3.2.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,6 +117,7 @@ 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 */],
@@ -129,6 +130,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
129
130
  ["LEAST", "LEAST" /* LEAST */],
130
131
  ["LPAD", "LPAD" /* LPAD */],
131
132
  ["RPAD", "RPAD" /* RPAD */],
133
+ ["TRANSLATE", "TRANSLATE" /* TRANSLATE */],
132
134
  ["CAST", "CAST" /* CAST */],
133
135
  ["CONVERT", "CONVERT" /* CONVERT */],
134
136
  ["FORMAT", "FORMAT" /* FORMAT */],
@@ -488,10 +490,12 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
488
490
  "LTRIM" /* LTRIM */,
489
491
  "RTRIM" /* RTRIM */,
490
492
  "LENGTH" /* LENGTH */,
493
+ "LENGTH_CHAR" /* LENGTH_CHAR */,
491
494
  "SUBSTRING" /* SUBSTRING */,
492
495
  "SUBSTR" /* SUBSTR */,
493
496
  "CONCAT" /* CONCAT */,
494
497
  "REPLACE" /* REPLACE */,
498
+ "TRANSLATE" /* TRANSLATE */,
495
499
  "COALESCE" /* COALESCE */,
496
500
  "NULLIF" /* NULLIF */,
497
501
  "ISNULL" /* ISNULL */,
@@ -543,6 +547,7 @@ var ParseError = class extends Error {
543
547
  var Parser = class {
544
548
  constructor(tokens) {
545
549
  this.tokens = tokens;
550
+ this.allowUnaryPlusNumber = false;
546
551
  this.pos = 0;
547
552
  /** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
548
553
  this.cteNames = /* @__PURE__ */ new Set();
@@ -1311,8 +1316,16 @@ var Parser = class {
1311
1316
  this.expect(")" /* RPAREN */);
1312
1317
  return expr;
1313
1318
  }
1319
+ if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
1320
+ this.advance();
1321
+ 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) };
1323
+ }
1314
1324
  if (this.peek().kind === "-" /* MINUS */) {
1315
1325
  this.advance();
1326
+ if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
1327
+ 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
+ }
1316
1329
  const operand = this.parseArithPrimary();
1317
1330
  if (operand.type === "NUMBER") return { type: "NUMBER", value: -operand.value };
1318
1331
  return { type: "ARITH", left: { type: "NUMBER", value: 0 }, op: "-", right: operand };
@@ -1422,10 +1435,12 @@ var Parser = class {
1422
1435
  ["LTRIM" /* LTRIM */]: "LTRIM",
1423
1436
  ["RTRIM" /* RTRIM */]: "RTRIM",
1424
1437
  ["LENGTH" /* LENGTH */]: "LENGTH",
1438
+ ["LENGTH_CHAR" /* LENGTH_CHAR */]: "LENGTH_CHAR",
1425
1439
  ["SUBSTRING" /* SUBSTRING */]: "SUBSTRING",
1426
1440
  ["SUBSTR" /* SUBSTR */]: "SUBSTRING",
1427
1441
  ["CONCAT" /* CONCAT */]: "CONCAT",
1428
1442
  ["REPLACE" /* REPLACE */]: "REPLACE",
1443
+ ["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
1429
1444
  ["COALESCE" /* COALESCE */]: "COALESCE",
1430
1445
  ["NULLIF" /* NULLIF */]: "NULLIF",
1431
1446
  ["ISNULL" /* ISNULL */]: "ISNULL",
@@ -2186,6 +2201,11 @@ var Parser = class {
2186
2201
  } else if (this.peek().kind === "IF" /* IF */) {
2187
2202
  const expr = this.parseIfExpr();
2188
2203
  row.push({ type: "CASE_VALUE", expr });
2204
+ } else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
2205
+ const sign = this.advance();
2206
+ 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 });
2189
2209
  } else {
2190
2210
  const tok = this.advance();
2191
2211
  if (tok.kind === "STRING" /* STRING */) {
@@ -2215,6 +2235,12 @@ var Parser = class {
2215
2235
  const { appId, subtableCode } = extractTableRef(name, this.prev());
2216
2236
  this.expect("SET" /* SET */);
2217
2237
  const assignments = this.parseAssignments();
2238
+ if (subtableCode && assignments.some((a) => a.value.type === "STRING_FUNC")) {
2239
+ throw new ParseError(
2240
+ "\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",
2241
+ this.prev()
2242
+ );
2243
+ }
2218
2244
  let from = null;
2219
2245
  if (this.consume("FROM" /* FROM */)) {
2220
2246
  const table = this.parseTableRef();
@@ -2258,7 +2284,14 @@ var Parser = class {
2258
2284
  from.targetFilter = decomposed.targetFilter;
2259
2285
  } else if (assignments.some((a) => a.value.type === "SOURCE_FIELD")) {
2260
2286
  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",
2287
+ "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",
2288
+ whereTok
2289
+ );
2290
+ } else if (assignments.some(
2291
+ (a) => a.value.type === "STRING_FUNC" && this.nodeContainsAnyQualifier(a.value)
2292
+ )) {
2293
+ throw new ParseError(
2294
+ "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
2295
  whereTok
2263
2296
  );
2264
2297
  }
@@ -2325,6 +2358,12 @@ var Parser = class {
2325
2358
  }
2326
2359
  validateUpdateFromAssignments(assignments, sourceAlias, tok) {
2327
2360
  for (const assignment of assignments) {
2361
+ if (assignment.value.type === "STRING_FUNC") {
2362
+ throw new ParseError(
2363
+ "UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
2364
+ tok
2365
+ );
2366
+ }
2328
2367
  if (assignment.value.type === "SOURCE_FIELD") {
2329
2368
  if (assignment.value.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
2330
2369
  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 +2503,17 @@ var Parser = class {
2464
2503
  this.expect(")" /* RPAREN */);
2465
2504
  return { type: "SCALAR_SUBQUERY", query };
2466
2505
  }
2467
- const node = this.parseArithAddSub();
2506
+ const previousAllowUnaryPlusNumber = this.allowUnaryPlusNumber;
2507
+ this.allowUnaryPlusNumber = true;
2508
+ let node;
2509
+ try {
2510
+ node = this.parseArithAddSub();
2511
+ } finally {
2512
+ this.allowUnaryPlusNumber = previousAllowUnaryPlusNumber;
2513
+ }
2468
2514
  if (node.type === "NUMBER") return node;
2469
2515
  if (node.type === "ARITH") return node;
2516
+ if (node.type === "STRING_FUNC") return node;
2470
2517
  if (node.type === "FIELD_REF") {
2471
2518
  const dot = node.field.indexOf(".");
2472
2519
  if (dot > 0 && dot < node.field.length - 1) {
@@ -2474,7 +2521,7 @@ var Parser = class {
2474
2521
  }
2475
2522
  }
2476
2523
  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",
2524
+ "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
2525
  tok
2479
2526
  );
2480
2527
  }
@@ -4499,6 +4546,43 @@ function applyRoundOp(op, num, digits) {
4499
4546
  if (digits > 0) return String(parseFloat(raw.toFixed(digits)));
4500
4547
  return String(raw);
4501
4548
  }
4549
+ function isHighSurrogate(codeUnit) {
4550
+ return codeUnit >= 55296 && codeUnit <= 56319;
4551
+ }
4552
+ function isLowSurrogate(codeUnit) {
4553
+ return codeUnit >= 56320 && codeUnit <= 57343;
4554
+ }
4555
+ function splitsSurrogatePair(value, index) {
4556
+ return index > 0 && index < value.length && isHighSurrogate(value.charCodeAt(index - 1)) && isLowSurrogate(value.charCodeAt(index));
4557
+ }
4558
+ function normalizeSliceIndex(index, length) {
4559
+ if (Number.isNaN(index) || index === Number.NEGATIVE_INFINITY) return 0;
4560
+ if (index === Number.POSITIVE_INFINITY) return length;
4561
+ const integer = Math.trunc(index);
4562
+ return integer < 0 ? Math.max(length + integer, 0) : Math.min(integer, length);
4563
+ }
4564
+ function sliceSafePrefix(value, budget) {
4565
+ let end = Math.min(Math.max(0, budget), value.length);
4566
+ if (splitsSurrogatePair(value, end)) end -= 1;
4567
+ return value.slice(0, end);
4568
+ }
4569
+ function sliceSafeSuffix(value, budget) {
4570
+ let start = Math.max(0, value.length - budget);
4571
+ if (splitsSurrogatePair(value, start)) start += 1;
4572
+ return value.slice(start);
4573
+ }
4574
+ function sliceSafeRange(value, rawStart, rawEnd) {
4575
+ let start = normalizeSliceIndex(rawStart, value.length);
4576
+ let end = normalizeSliceIndex(rawEnd, value.length);
4577
+ if (end <= start) return "";
4578
+ if (splitsSurrogatePair(value, start)) start += 1;
4579
+ if (splitsSurrogatePair(value, end)) end -= 1;
4580
+ return value.slice(start, Math.max(start, end));
4581
+ }
4582
+ function makeSafePadding(pad, gap) {
4583
+ const repeated = pad.repeat(Math.ceil(gap / pad.length));
4584
+ return sliceSafePrefix(repeated, gap);
4585
+ }
4502
4586
  function evalStringFunc(expr, row) {
4503
4587
  const args = expr.args.map((a) => evalStringFuncArg(a, row));
4504
4588
  switch (expr.func) {
@@ -4514,23 +4598,26 @@ function evalStringFunc(expr, row) {
4514
4598
  return (args[0] ?? "").trimEnd();
4515
4599
  case "LENGTH":
4516
4600
  return String((args[0] ?? "").length);
4601
+ case "LENGTH_CHAR":
4602
+ assertArity("LENGTH_CHAR", args, 1, 1);
4603
+ return String([...args[0] ?? ""].length);
4517
4604
  case "SUBSTRING": {
4518
4605
  const str = args[0] ?? "";
4519
4606
  const start = Math.max(0, Number(args[1] ?? "1") - 1);
4520
4607
  const len = args[2] !== void 0 ? Number(args[2]) : void 0;
4521
- return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
4608
+ return sliceSafeRange(str, start, len !== void 0 ? start + len : str.length);
4522
4609
  }
4523
4610
  case "LEFT": {
4524
4611
  assertArity("LEFT", args, 2, 2);
4525
4612
  const str = args[0];
4526
4613
  const n = Math.trunc(Number(args[1]));
4527
- return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
4614
+ return Number.isNaN(n) || n <= 0 ? "" : sliceSafePrefix(str, n);
4528
4615
  }
4529
4616
  case "RIGHT": {
4530
4617
  assertArity("RIGHT", args, 2, 2);
4531
4618
  const str = args[0];
4532
4619
  const n = Math.trunc(Number(args[1]));
4533
- return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
4620
+ return Number.isNaN(n) || n <= 0 ? "" : sliceSafeSuffix(str, n);
4534
4621
  }
4535
4622
  case "INSTR":
4536
4623
  assertArity("INSTR", args, 2, 2);
@@ -4541,10 +4628,11 @@ function evalStringFunc(expr, row) {
4541
4628
  const str = args[0];
4542
4629
  const n = Math.trunc(Number(args[1]));
4543
4630
  if (Number.isNaN(n) || n <= 0) return "";
4544
- if (str.length >= n) return str.slice(0, n);
4631
+ if (str.length >= n) return sliceSafePrefix(str, n);
4545
4632
  const pad = args[2] ?? " ";
4546
4633
  if (pad === "") return str;
4547
- return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
4634
+ const padding = makeSafePadding(pad, n - str.length);
4635
+ return expr.func === "LPAD" ? padding + str : str + padding;
4548
4636
  }
4549
4637
  case "GREATEST":
4550
4638
  case "LEAST":
@@ -4558,6 +4646,21 @@ function evalStringFunc(expr, row) {
4558
4646
  const to = args[2] ?? "";
4559
4647
  return from === "" ? str : str.split(from).join(to);
4560
4648
  }
4649
+ case "TRANSLATE": {
4650
+ assertArity("TRANSLATE", args, 3, 3);
4651
+ const from = [...args[1]];
4652
+ const to = [...args[2]];
4653
+ if (from.length !== to.length) {
4654
+ throw new Error(
4655
+ `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`
4656
+ );
4657
+ }
4658
+ const map = /* @__PURE__ */ new Map();
4659
+ from.forEach((ch, i) => {
4660
+ if (!map.has(ch)) map.set(ch, to[i]);
4661
+ });
4662
+ return [...args[0]].map((ch) => map.get(ch) ?? ch).join("");
4663
+ }
4561
4664
  case "COALESCE":
4562
4665
  return args.find((a) => a !== "") ?? "";
4563
4666
  case "NULLIF":
@@ -4795,6 +4898,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
4795
4898
  }
4796
4899
  var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
4797
4900
  "LENGTH",
4901
+ "LENGTH_CHAR",
4798
4902
  "INSTR",
4799
4903
  "ROUND",
4800
4904
  "FLOOR",
@@ -5042,7 +5146,7 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
5042
5146
  function buildUpdateRecord(assignments, fieldTypes) {
5043
5147
  const record = {};
5044
5148
  for (const { field, value } of assignments) {
5045
- if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "SOURCE_FIELD") continue;
5149
+ if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
5046
5150
  record[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
5047
5151
  }
5048
5152
  return record;
@@ -5052,12 +5156,19 @@ function hasArithAssignment(stmt) {
5052
5156
  (a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
5053
5157
  );
5054
5158
  }
5159
+ function hasRowDependentAssignment(stmt) {
5160
+ return stmt.assignments.some(
5161
+ (a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
5162
+ );
5163
+ }
5055
5164
  function updateToGetQueryForArith(stmt) {
5056
5165
  assertDmlWhereIsSafe(stmt.where);
5057
5166
  const refFields = /* @__PURE__ */ new Set();
5058
5167
  for (const { value } of stmt.assignments) {
5059
5168
  if (value.type === "ARITH") {
5060
5169
  collectArithFields2(value, refFields);
5170
+ } else if (value.type === "STRING_FUNC") {
5171
+ collectStringFuncFields2(value, refFields);
5061
5172
  } else if (value.type === "CASE_VALUE") {
5062
5173
  collectCaseFields(value.expr, refFields);
5063
5174
  }
@@ -5144,6 +5255,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
5144
5255
  for (const { field, value } of stmt.assignments) {
5145
5256
  if (value.type === "ARITH") {
5146
5257
  record[field] = { value: String(evalArith(value, raw)) };
5258
+ } else if (value.type === "STRING_FUNC") {
5259
+ record[field] = { value: evalStringFunc(value, row) };
5147
5260
  } else if (value.type === "CASE_VALUE") {
5148
5261
  record[field] = { value: evalCaseWhenValue(value.expr, row, fieldTypes.get(field)) };
5149
5262
  } else if (value.type === "SOURCE_FIELD") {
@@ -5192,6 +5305,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
5192
5305
  throw new DmlConvertError(`\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u306B\u5909\u63DB\u3067\u304D\u306A\u3044\u5024\u3067\u3059: ${raw}`);
5193
5306
  }
5194
5307
  record[field] = { value: toKintoneValue({ type: "STRING", value: raw }, fieldType) };
5308
+ } else if (value.type === "STRING_FUNC") {
5309
+ 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
5310
  } else if (value.type === "ARITH") {
5196
5311
  record[field] = { value: String(evalArith(value, target)) };
5197
5312
  } else if (value.type === "CASE_VALUE") {
@@ -6083,6 +6198,7 @@ function compareSortKeys(a, b, meta) {
6083
6198
  }
6084
6199
  var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
6085
6200
  "LENGTH",
6201
+ "LENGTH_CHAR",
6086
6202
  "INSTR",
6087
6203
  "ROUND",
6088
6204
  "FLOOR",
@@ -8303,6 +8419,7 @@ function systemColumnMeta(field) {
8303
8419
  }
8304
8420
  var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
8305
8421
  "LENGTH",
8422
+ "LENGTH_CHAR",
8306
8423
  "INSTR",
8307
8424
  "ROUND",
8308
8425
  "FLOOR",
@@ -9270,6 +9387,28 @@ var NON_WRITABLE_FIELD_TYPES = /* @__PURE__ */ new Set([
9270
9387
  "CATEGORY",
9271
9388
  "REFERENCE_TABLE"
9272
9389
  ]);
9390
+ function assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos) {
9391
+ const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9392
+ for (const code of targetFields) {
9393
+ const info = infoByCode.get(code);
9394
+ if (!info) {
9395
+ throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
9396
+ }
9397
+ if (info.inSubtable) {
9398
+ throw new Error(
9399
+ `ArgumentError: DML target field ${code} is inside a subtable. Use subtable DML syntax (for example, APP${appId}$\u30C6\u30FC\u30D6\u30EB).`
9400
+ );
9401
+ }
9402
+ if (info.writable === false || NON_WRITABLE_FIELD_TYPES.has(info.fieldType)) {
9403
+ throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
9404
+ }
9405
+ }
9406
+ }
9407
+ async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheContext) {
9408
+ const fieldInfos = await getFieldsCached(appId, client, cacheContext);
9409
+ assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
9410
+ return fieldInfos;
9411
+ }
9273
9412
  async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
9274
9413
  return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
9275
9414
  }
@@ -9281,24 +9420,22 @@ var RejectLimitExceededError = class extends Error {
9281
9420
  }
9282
9421
  };
9283
9422
  async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
9284
- if (stmt.type === "UPDATE") {
9285
- await assertDmlWhereCapability(stmt, client, cacheContext);
9286
- }
9287
9423
  const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
9288
9424
  const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
9289
9425
  if (new Set(payloadFields).size !== payloadFields.length) {
9290
9426
  throw new Error("ArgumentError: DML target fields contain duplicates.");
9291
9427
  }
9292
- const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
9293
- const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9294
9428
  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
- }
9429
+ const fieldInfos = await loadWritableTopLevelDmlFields(
9430
+ stmt.appId,
9431
+ targetFields,
9432
+ client,
9433
+ cacheContext
9434
+ );
9435
+ if (stmt.type === "UPDATE") {
9436
+ await assertDmlWhereCapability(stmt, client, cacheContext);
9301
9437
  }
9438
+ const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9302
9439
  const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
9303
9440
  const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
9304
9441
  candidates,
@@ -9467,7 +9604,7 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
9467
9604
  await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
9468
9605
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9469
9606
  let records;
9470
- if (hasArithAssignment(stmt)) {
9607
+ if (hasRowDependentAssignment(stmt)) {
9471
9608
  const getParams = updateToGetQueryForArith(stmt);
9472
9609
  const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [...getParams.fields], {
9473
9610
  maxRecords: options.maxRecords ?? 1e4,
@@ -9676,6 +9813,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
9676
9813
  if (stmt.subtableCode) {
9677
9814
  return executeInsertSubtable(stmt, client, options, cacheContext);
9678
9815
  }
9816
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
9679
9817
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9680
9818
  const batches = insertToPostBatches(stmt, fieldTypes);
9681
9819
  const createdIds = [];
@@ -9690,6 +9828,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
9690
9828
  };
9691
9829
  }
9692
9830
  async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
9831
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
9693
9832
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
9694
9833
  const { rows, columns } = selectResult;
9695
9834
  if (columns.length !== stmt.fields.length) {
@@ -9724,17 +9863,24 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
9724
9863
  };
9725
9864
  }
9726
9865
  async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9727
- await assertDmlWhereCapability(stmt, client, cacheContext);
9728
9866
  if (stmt.subtableCode) {
9867
+ await assertDmlWhereCapability(stmt, client, cacheContext);
9729
9868
  return executeUpdateSubtable(stmt, client, options, cacheContext);
9730
9869
  }
9870
+ await loadWritableTopLevelDmlFields(
9871
+ stmt.appId,
9872
+ stmt.assignments.map((assignment) => assignment.field),
9873
+ client,
9874
+ cacheContext
9875
+ );
9876
+ await assertDmlWhereCapability(stmt, client, cacheContext);
9731
9877
  if (stmt.from != null) {
9732
9878
  return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
9733
9879
  }
9734
9880
  const maxRecords = options.maxRecords ?? 1e4;
9735
9881
  await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
9736
9882
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9737
- if (hasArithAssignment(stmt)) {
9883
+ if (hasRowDependentAssignment(stmt)) {
9738
9884
  const getParams2 = updateToGetQueryForArith(stmt);
9739
9885
  const resolved2 = await fetchRecordsForSharedPlan(
9740
9886
  client.getRecords,
@@ -9828,6 +9974,7 @@ async function executeDelete(stmt, client, options, cacheContext) {
9828
9974
  return { type: "DELETE", deletedCount: ids.length };
9829
9975
  }
9830
9976
  async function executeUpsert(stmt, client, options, cacheContext) {
9977
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
9831
9978
  const toInsert = [];
9832
9979
  const toUpdate = [];
9833
9980
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
@@ -10254,6 +10401,7 @@ function evalOrderKeyForRow(key, row) {
10254
10401
  }
10255
10402
  }
10256
10403
  async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
10404
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10257
10405
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
10258
10406
  const { rows, columns } = selectResult;
10259
10407
  if (columns.length !== stmt.fields.length) {
@@ -10889,6 +11037,8 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
10889
11037
  }
10890
11038
  function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
10891
11039
  const isArith = hasArithAssignment(stmt);
11040
+ const isStringFunc = stmt.assignments.some((a) => a.value.type === "STRING_FUNC");
11041
+ const isRowDependent = hasRowDependentAssignment(stmt);
10892
11042
  const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
10893
11043
  const lines = [];
10894
11044
  if (label) lines.push(label);
@@ -10905,10 +11055,11 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
10905
11055
  lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
10906
11056
  const setTypes = [];
10907
11057
  if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
11058
+ if (isStringFunc) setTypes.push("\u6587\u5B57\u5217\u95A2\u6570 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A55\u4FA1\uFF09");
10908
11059
  if (isSubq) setTypes.push("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA SET");
10909
- if (!isArith && !isSubq) setTypes.push("\u5358\u7D14 SET");
11060
+ if (!isRowDependent && !isSubq) setTypes.push("\u5358\u7D14 SET");
10910
11061
  lines.push(` set type: ${setTypes.join(", ")}`);
10911
- if (isArith) {
11062
+ if (isRowDependent) {
10912
11063
  const refFields = collectArithRefFields(stmt);
10913
11064
  if (refFields.length > 0) {
10914
11065
  lines.push(` ref fields: ${refFields.join(", ")}\uFF08GET \u306B\u542B\u3081\u308B\uFF09`);
@@ -10994,6 +11145,7 @@ function collectArithRefFields(stmt) {
10994
11145
  const refs = /* @__PURE__ */ new Set();
10995
11146
  for (const { value } of stmt.assignments) {
10996
11147
  if (value.type === "ARITH") collectArithNodeRefs(value, refs);
11148
+ if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
10997
11149
  }
10998
11150
  return [...refs];
10999
11151
  }
@@ -11006,6 +11158,13 @@ function collectArithNodeRefs(node, out) {
11006
11158
  collectArithNodeRefs(node.left, out);
11007
11159
  collectArithNodeRefs(node.right, out);
11008
11160
  }
11161
+ if (node.type === "STRING_FUNC") {
11162
+ for (const arg of node.args) {
11163
+ if (arg.type !== "STRING" && arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") {
11164
+ collectArithNodeRefs(arg, out);
11165
+ }
11166
+ }
11167
+ }
11009
11168
  }
11010
11169
  function formatAssignment(a) {
11011
11170
  const v = a.value;
@@ -11013,6 +11172,7 @@ function formatAssignment(a) {
11013
11172
  if (v.type === "NUMBER") return `${a.field} = ${v.value}`;
11014
11173
  if (v.type === "ARITH") return `${a.field} = ${formatArithExprStr(v)}`;
11015
11174
  if (v.type === "CASE_VALUE") return `${a.field} = CASE WHEN ...`;
11175
+ if (v.type === "STRING_FUNC") return `${a.field} = ${v.func}(...)`;
11016
11176
  if (v.type === "SCALAR_SUBQUERY") return `${a.field} = (SELECT ...)`;
11017
11177
  if (v.type === "SOURCE_FIELD") return `${a.field} = ${v.alias}.${v.field}`;
11018
11178
  return `${a.field} = (${v.type})`;
@@ -31029,6 +31029,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31029
31029
  ["LTRIM", "LTRIM" /* LTRIM */],
31030
31030
  ["RTRIM", "RTRIM" /* RTRIM */],
31031
31031
  ["LENGTH", "LENGTH" /* LENGTH */],
31032
+ ["LENGTH_CHAR", "LENGTH_CHAR" /* LENGTH_CHAR */],
31032
31033
  ["SUBSTRING", "SUBSTRING" /* SUBSTRING */],
31033
31034
  ["SUBSTR", "SUBSTR" /* SUBSTR */],
31034
31035
  ["CONCAT", "CONCAT" /* CONCAT */],
@@ -31041,6 +31042,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31041
31042
  ["LEAST", "LEAST" /* LEAST */],
31042
31043
  ["LPAD", "LPAD" /* LPAD */],
31043
31044
  ["RPAD", "RPAD" /* RPAD */],
31045
+ ["TRANSLATE", "TRANSLATE" /* TRANSLATE */],
31044
31046
  ["CAST", "CAST" /* CAST */],
31045
31047
  ["CONVERT", "CONVERT" /* CONVERT */],
31046
31048
  ["FORMAT", "FORMAT" /* FORMAT */],
@@ -31400,10 +31402,12 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31400
31402
  "LTRIM" /* LTRIM */,
31401
31403
  "RTRIM" /* RTRIM */,
31402
31404
  "LENGTH" /* LENGTH */,
31405
+ "LENGTH_CHAR" /* LENGTH_CHAR */,
31403
31406
  "SUBSTRING" /* SUBSTRING */,
31404
31407
  "SUBSTR" /* SUBSTR */,
31405
31408
  "CONCAT" /* CONCAT */,
31406
31409
  "REPLACE" /* REPLACE */,
31410
+ "TRANSLATE" /* TRANSLATE */,
31407
31411
  "COALESCE" /* COALESCE */,
31408
31412
  "NULLIF" /* NULLIF */,
31409
31413
  "ISNULL" /* ISNULL */,
@@ -31455,6 +31459,7 @@ var ParseError = class extends Error {
31455
31459
  var Parser = class {
31456
31460
  constructor(tokens) {
31457
31461
  this.tokens = tokens;
31462
+ this.allowUnaryPlusNumber = false;
31458
31463
  this.pos = 0;
31459
31464
  /** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
31460
31465
  this.cteNames = /* @__PURE__ */ new Set();
@@ -32223,8 +32228,16 @@ var Parser = class {
32223
32228
  this.expect(")" /* RPAREN */);
32224
32229
  return expr;
32225
32230
  }
32231
+ if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
32232
+ this.advance();
32233
+ const number4 = this.expect("NUMBER" /* NUMBER */, "\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
32234
+ return { type: "NUMBER", value: Number(number4.value) };
32235
+ }
32226
32236
  if (this.peek().kind === "-" /* MINUS */) {
32227
32237
  this.advance();
32238
+ if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
32239
+ 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());
32240
+ }
32228
32241
  const operand = this.parseArithPrimary();
32229
32242
  if (operand.type === "NUMBER") return { type: "NUMBER", value: -operand.value };
32230
32243
  return { type: "ARITH", left: { type: "NUMBER", value: 0 }, op: "-", right: operand };
@@ -32334,10 +32347,12 @@ var Parser = class {
32334
32347
  ["LTRIM" /* LTRIM */]: "LTRIM",
32335
32348
  ["RTRIM" /* RTRIM */]: "RTRIM",
32336
32349
  ["LENGTH" /* LENGTH */]: "LENGTH",
32350
+ ["LENGTH_CHAR" /* LENGTH_CHAR */]: "LENGTH_CHAR",
32337
32351
  ["SUBSTRING" /* SUBSTRING */]: "SUBSTRING",
32338
32352
  ["SUBSTR" /* SUBSTR */]: "SUBSTRING",
32339
32353
  ["CONCAT" /* CONCAT */]: "CONCAT",
32340
32354
  ["REPLACE" /* REPLACE */]: "REPLACE",
32355
+ ["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
32341
32356
  ["COALESCE" /* COALESCE */]: "COALESCE",
32342
32357
  ["NULLIF" /* NULLIF */]: "NULLIF",
32343
32358
  ["ISNULL" /* ISNULL */]: "ISNULL",
@@ -33098,6 +33113,11 @@ var Parser = class {
33098
33113
  } else if (this.peek().kind === "IF" /* IF */) {
33099
33114
  const expr = this.parseIfExpr();
33100
33115
  row.push({ type: "CASE_VALUE", expr });
33116
+ } else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
33117
+ const sign = this.advance();
33118
+ const number4 = 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");
33119
+ const value = Number(number4.value);
33120
+ row.push({ type: "NUMBER", value: sign.kind === "-" /* MINUS */ ? -value : value });
33101
33121
  } else {
33102
33122
  const tok = this.advance();
33103
33123
  if (tok.kind === "STRING" /* STRING */) {
@@ -33127,6 +33147,12 @@ var Parser = class {
33127
33147
  const { appId, subtableCode } = extractTableRef(name, this.prev());
33128
33148
  this.expect("SET" /* SET */);
33129
33149
  const assignments = this.parseAssignments();
33150
+ if (subtableCode && assignments.some((a) => a.value.type === "STRING_FUNC")) {
33151
+ throw new ParseError(
33152
+ "\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",
33153
+ this.prev()
33154
+ );
33155
+ }
33130
33156
  let from = null;
33131
33157
  if (this.consume("FROM" /* FROM */)) {
33132
33158
  const table = this.parseTableRef();
@@ -33170,7 +33196,14 @@ var Parser = class {
33170
33196
  from.targetFilter = decomposed.targetFilter;
33171
33197
  } else if (assignments.some((a) => a.value.type === "SOURCE_FIELD")) {
33172
33198
  throw new ParseError(
33173
- "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",
33199
+ "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",
33200
+ whereTok
33201
+ );
33202
+ } else if (assignments.some(
33203
+ (a) => a.value.type === "STRING_FUNC" && this.nodeContainsAnyQualifier(a.value)
33204
+ )) {
33205
+ throw new ParseError(
33206
+ "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",
33174
33207
  whereTok
33175
33208
  );
33176
33209
  }
@@ -33237,6 +33270,12 @@ var Parser = class {
33237
33270
  }
33238
33271
  validateUpdateFromAssignments(assignments, sourceAlias, tok) {
33239
33272
  for (const assignment of assignments) {
33273
+ if (assignment.value.type === "STRING_FUNC") {
33274
+ throw new ParseError(
33275
+ "UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
33276
+ tok
33277
+ );
33278
+ }
33240
33279
  if (assignment.value.type === "SOURCE_FIELD") {
33241
33280
  if (assignment.value.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
33242
33281
  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);
@@ -33376,9 +33415,17 @@ var Parser = class {
33376
33415
  this.expect(")" /* RPAREN */);
33377
33416
  return { type: "SCALAR_SUBQUERY", query };
33378
33417
  }
33379
- const node = this.parseArithAddSub();
33418
+ const previousAllowUnaryPlusNumber = this.allowUnaryPlusNumber;
33419
+ this.allowUnaryPlusNumber = true;
33420
+ let node;
33421
+ try {
33422
+ node = this.parseArithAddSub();
33423
+ } finally {
33424
+ this.allowUnaryPlusNumber = previousAllowUnaryPlusNumber;
33425
+ }
33380
33426
  if (node.type === "NUMBER") return node;
33381
33427
  if (node.type === "ARITH") return node;
33428
+ if (node.type === "STRING_FUNC") return node;
33382
33429
  if (node.type === "FIELD_REF") {
33383
33430
  const dot = node.field.indexOf(".");
33384
33431
  if (dot > 0 && dot < node.field.length - 1) {
@@ -33386,7 +33433,7 @@ var Parser = class {
33386
33433
  }
33387
33434
  }
33388
33435
  throw new ParseError(
33389
- "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",
33436
+ "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",
33390
33437
  tok
33391
33438
  );
33392
33439
  }
@@ -35399,6 +35446,43 @@ function applyRoundOp(op, num, digits) {
35399
35446
  if (digits > 0) return String(parseFloat(raw.toFixed(digits)));
35400
35447
  return String(raw);
35401
35448
  }
35449
+ function isHighSurrogate(codeUnit) {
35450
+ return codeUnit >= 55296 && codeUnit <= 56319;
35451
+ }
35452
+ function isLowSurrogate(codeUnit) {
35453
+ return codeUnit >= 56320 && codeUnit <= 57343;
35454
+ }
35455
+ function splitsSurrogatePair(value, index) {
35456
+ return index > 0 && index < value.length && isHighSurrogate(value.charCodeAt(index - 1)) && isLowSurrogate(value.charCodeAt(index));
35457
+ }
35458
+ function normalizeSliceIndex(index, length) {
35459
+ if (Number.isNaN(index) || index === Number.NEGATIVE_INFINITY) return 0;
35460
+ if (index === Number.POSITIVE_INFINITY) return length;
35461
+ const integer2 = Math.trunc(index);
35462
+ return integer2 < 0 ? Math.max(length + integer2, 0) : Math.min(integer2, length);
35463
+ }
35464
+ function sliceSafePrefix(value, budget) {
35465
+ let end = Math.min(Math.max(0, budget), value.length);
35466
+ if (splitsSurrogatePair(value, end)) end -= 1;
35467
+ return value.slice(0, end);
35468
+ }
35469
+ function sliceSafeSuffix(value, budget) {
35470
+ let start = Math.max(0, value.length - budget);
35471
+ if (splitsSurrogatePair(value, start)) start += 1;
35472
+ return value.slice(start);
35473
+ }
35474
+ function sliceSafeRange(value, rawStart, rawEnd) {
35475
+ let start = normalizeSliceIndex(rawStart, value.length);
35476
+ let end = normalizeSliceIndex(rawEnd, value.length);
35477
+ if (end <= start) return "";
35478
+ if (splitsSurrogatePair(value, start)) start += 1;
35479
+ if (splitsSurrogatePair(value, end)) end -= 1;
35480
+ return value.slice(start, Math.max(start, end));
35481
+ }
35482
+ function makeSafePadding(pad, gap) {
35483
+ const repeated = pad.repeat(Math.ceil(gap / pad.length));
35484
+ return sliceSafePrefix(repeated, gap);
35485
+ }
35402
35486
  function evalStringFunc(expr, row) {
35403
35487
  const args = expr.args.map((a) => evalStringFuncArg(a, row));
35404
35488
  switch (expr.func) {
@@ -35414,23 +35498,26 @@ function evalStringFunc(expr, row) {
35414
35498
  return (args[0] ?? "").trimEnd();
35415
35499
  case "LENGTH":
35416
35500
  return String((args[0] ?? "").length);
35501
+ case "LENGTH_CHAR":
35502
+ assertArity("LENGTH_CHAR", args, 1, 1);
35503
+ return String([...args[0] ?? ""].length);
35417
35504
  case "SUBSTRING": {
35418
35505
  const str = args[0] ?? "";
35419
35506
  const start = Math.max(0, Number(args[1] ?? "1") - 1);
35420
35507
  const len = args[2] !== void 0 ? Number(args[2]) : void 0;
35421
- return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
35508
+ return sliceSafeRange(str, start, len !== void 0 ? start + len : str.length);
35422
35509
  }
35423
35510
  case "LEFT": {
35424
35511
  assertArity("LEFT", args, 2, 2);
35425
35512
  const str = args[0];
35426
35513
  const n = Math.trunc(Number(args[1]));
35427
- return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
35514
+ return Number.isNaN(n) || n <= 0 ? "" : sliceSafePrefix(str, n);
35428
35515
  }
35429
35516
  case "RIGHT": {
35430
35517
  assertArity("RIGHT", args, 2, 2);
35431
35518
  const str = args[0];
35432
35519
  const n = Math.trunc(Number(args[1]));
35433
- return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
35520
+ return Number.isNaN(n) || n <= 0 ? "" : sliceSafeSuffix(str, n);
35434
35521
  }
35435
35522
  case "INSTR":
35436
35523
  assertArity("INSTR", args, 2, 2);
@@ -35441,10 +35528,11 @@ function evalStringFunc(expr, row) {
35441
35528
  const str = args[0];
35442
35529
  const n = Math.trunc(Number(args[1]));
35443
35530
  if (Number.isNaN(n) || n <= 0) return "";
35444
- if (str.length >= n) return str.slice(0, n);
35531
+ if (str.length >= n) return sliceSafePrefix(str, n);
35445
35532
  const pad = args[2] ?? " ";
35446
35533
  if (pad === "") return str;
35447
- return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
35534
+ const padding = makeSafePadding(pad, n - str.length);
35535
+ return expr.func === "LPAD" ? padding + str : str + padding;
35448
35536
  }
35449
35537
  case "GREATEST":
35450
35538
  case "LEAST":
@@ -35458,6 +35546,21 @@ function evalStringFunc(expr, row) {
35458
35546
  const to = args[2] ?? "";
35459
35547
  return from === "" ? str : str.split(from).join(to);
35460
35548
  }
35549
+ case "TRANSLATE": {
35550
+ assertArity("TRANSLATE", args, 3, 3);
35551
+ const from = [...args[1]];
35552
+ const to = [...args[2]];
35553
+ if (from.length !== to.length) {
35554
+ throw new Error(
35555
+ `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`
35556
+ );
35557
+ }
35558
+ const map2 = /* @__PURE__ */ new Map();
35559
+ from.forEach((ch, i) => {
35560
+ if (!map2.has(ch)) map2.set(ch, to[i]);
35561
+ });
35562
+ return [...args[0]].map((ch) => map2.get(ch) ?? ch).join("");
35563
+ }
35461
35564
  case "COALESCE":
35462
35565
  return args.find((a) => a !== "") ?? "";
35463
35566
  case "NULLIF":
@@ -35695,6 +35798,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
35695
35798
  }
35696
35799
  var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
35697
35800
  "LENGTH",
35801
+ "LENGTH_CHAR",
35698
35802
  "INSTR",
35699
35803
  "ROUND",
35700
35804
  "FLOOR",
@@ -35942,7 +36046,7 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
35942
36046
  function buildUpdateRecord(assignments, fieldTypes) {
35943
36047
  const record2 = {};
35944
36048
  for (const { field, value } of assignments) {
35945
- if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "SOURCE_FIELD") continue;
36049
+ if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
35946
36050
  record2[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
35947
36051
  }
35948
36052
  return record2;
@@ -35952,12 +36056,19 @@ function hasArithAssignment(stmt) {
35952
36056
  (a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
35953
36057
  );
35954
36058
  }
36059
+ function hasRowDependentAssignment(stmt) {
36060
+ return stmt.assignments.some(
36061
+ (a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
36062
+ );
36063
+ }
35955
36064
  function updateToGetQueryForArith(stmt) {
35956
36065
  assertDmlWhereIsSafe(stmt.where);
35957
36066
  const refFields = /* @__PURE__ */ new Set();
35958
36067
  for (const { value } of stmt.assignments) {
35959
36068
  if (value.type === "ARITH") {
35960
36069
  collectArithFields2(value, refFields);
36070
+ } else if (value.type === "STRING_FUNC") {
36071
+ collectStringFuncFields2(value, refFields);
35961
36072
  } else if (value.type === "CASE_VALUE") {
35962
36073
  collectCaseFields(value.expr, refFields);
35963
36074
  }
@@ -36044,6 +36155,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
36044
36155
  for (const { field, value } of stmt.assignments) {
36045
36156
  if (value.type === "ARITH") {
36046
36157
  record2[field] = { value: String(evalArith(value, raw)) };
36158
+ } else if (value.type === "STRING_FUNC") {
36159
+ record2[field] = { value: evalStringFunc(value, row) };
36047
36160
  } else if (value.type === "CASE_VALUE") {
36048
36161
  record2[field] = { value: evalCaseWhenValue(value.expr, row, fieldTypes.get(field)) };
36049
36162
  } else if (value.type === "SOURCE_FIELD") {
@@ -36092,6 +36205,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
36092
36205
  throw new DmlConvertError(`\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u306B\u5909\u63DB\u3067\u304D\u306A\u3044\u5024\u3067\u3059: ${raw}`);
36093
36206
  }
36094
36207
  record2[field] = { value: toKintoneValue({ type: "STRING", value: raw }, fieldType) };
36208
+ } else if (value.type === "STRING_FUNC") {
36209
+ throw new DmlConvertError("UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
36095
36210
  } else if (value.type === "ARITH") {
36096
36211
  record2[field] = { value: String(evalArith(value, target)) };
36097
36212
  } else if (value.type === "CASE_VALUE") {
@@ -36983,6 +37098,7 @@ function compareSortKeys(a, b, meta3) {
36983
37098
  }
36984
37099
  var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
36985
37100
  "LENGTH",
37101
+ "LENGTH_CHAR",
36986
37102
  "INSTR",
36987
37103
  "ROUND",
36988
37104
  "FLOOR",
@@ -39203,6 +39319,7 @@ function systemColumnMeta(field) {
39203
39319
  }
39204
39320
  var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
39205
39321
  "LENGTH",
39322
+ "LENGTH_CHAR",
39206
39323
  "INSTR",
39207
39324
  "ROUND",
39208
39325
  "FLOOR",
@@ -40170,6 +40287,28 @@ var NON_WRITABLE_FIELD_TYPES = /* @__PURE__ */ new Set([
40170
40287
  "CATEGORY",
40171
40288
  "REFERENCE_TABLE"
40172
40289
  ]);
40290
+ function assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos) {
40291
+ const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
40292
+ for (const code of targetFields) {
40293
+ const info = infoByCode.get(code);
40294
+ if (!info) {
40295
+ throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
40296
+ }
40297
+ if (info.inSubtable) {
40298
+ throw new Error(
40299
+ `ArgumentError: DML target field ${code} is inside a subtable. Use subtable DML syntax (for example, APP${appId}$\u30C6\u30FC\u30D6\u30EB).`
40300
+ );
40301
+ }
40302
+ if (info.writable === false || NON_WRITABLE_FIELD_TYPES.has(info.fieldType)) {
40303
+ throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
40304
+ }
40305
+ }
40306
+ }
40307
+ async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheContext) {
40308
+ const fieldInfos = await getFieldsCached(appId, client, cacheContext);
40309
+ assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
40310
+ return fieldInfos;
40311
+ }
40173
40312
  async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
40174
40313
  return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
40175
40314
  }
@@ -40181,24 +40320,22 @@ var RejectLimitExceededError = class extends Error {
40181
40320
  }
40182
40321
  };
40183
40322
  async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
40184
- if (stmt.type === "UPDATE") {
40185
- await assertDmlWhereCapability(stmt, client, cacheContext);
40186
- }
40187
40323
  const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
40188
40324
  const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
40189
40325
  if (new Set(payloadFields).size !== payloadFields.length) {
40190
40326
  throw new Error("ArgumentError: DML target fields contain duplicates.");
40191
40327
  }
40192
- const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
40193
- const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
40194
40328
  const targetFields = stmt.type === "UPDATE" ? stmt.assignments.map((a) => a.field) : stmt.fields;
40195
- for (const code of targetFields) {
40196
- const info = infoByCode.get(code);
40197
- if (!info) throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
40198
- if (info.writable === false || NON_WRITABLE_FIELD_TYPES.has(info.fieldType)) {
40199
- throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
40200
- }
40329
+ const fieldInfos = await loadWritableTopLevelDmlFields(
40330
+ stmt.appId,
40331
+ targetFields,
40332
+ client,
40333
+ cacheContext
40334
+ );
40335
+ if (stmt.type === "UPDATE") {
40336
+ await assertDmlWhereCapability(stmt, client, cacheContext);
40201
40337
  }
40338
+ const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
40202
40339
  const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
40203
40340
  const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
40204
40341
  candidates,
@@ -40367,7 +40504,7 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
40367
40504
  await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
40368
40505
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
40369
40506
  let records;
40370
- if (hasArithAssignment(stmt)) {
40507
+ if (hasRowDependentAssignment(stmt)) {
40371
40508
  const getParams = updateToGetQueryForArith(stmt);
40372
40509
  const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [...getParams.fields], {
40373
40510
  maxRecords: options.maxRecords ?? 1e4,
@@ -40576,6 +40713,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
40576
40713
  if (stmt.subtableCode) {
40577
40714
  return executeInsertSubtable(stmt, client, options, cacheContext);
40578
40715
  }
40716
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
40579
40717
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
40580
40718
  const batches = insertToPostBatches(stmt, fieldTypes);
40581
40719
  const createdIds = [];
@@ -40590,6 +40728,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
40590
40728
  };
40591
40729
  }
40592
40730
  async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
40731
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
40593
40732
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
40594
40733
  const { rows, columns } = selectResult;
40595
40734
  if (columns.length !== stmt.fields.length) {
@@ -40624,17 +40763,24 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
40624
40763
  };
40625
40764
  }
40626
40765
  async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
40627
- await assertDmlWhereCapability(stmt, client, cacheContext);
40628
40766
  if (stmt.subtableCode) {
40767
+ await assertDmlWhereCapability(stmt, client, cacheContext);
40629
40768
  return executeUpdateSubtable(stmt, client, options, cacheContext);
40630
40769
  }
40770
+ await loadWritableTopLevelDmlFields(
40771
+ stmt.appId,
40772
+ stmt.assignments.map((assignment) => assignment.field),
40773
+ client,
40774
+ cacheContext
40775
+ );
40776
+ await assertDmlWhereCapability(stmt, client, cacheContext);
40631
40777
  if (stmt.from != null) {
40632
40778
  return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
40633
40779
  }
40634
40780
  const maxRecords2 = options.maxRecords ?? 1e4;
40635
40781
  await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
40636
40782
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
40637
- if (hasArithAssignment(stmt)) {
40783
+ if (hasRowDependentAssignment(stmt)) {
40638
40784
  const getParams2 = updateToGetQueryForArith(stmt);
40639
40785
  const resolved2 = await fetchRecordsForSharedPlan(
40640
40786
  client.getRecords,
@@ -40728,6 +40874,7 @@ async function executeDelete(stmt, client, options, cacheContext) {
40728
40874
  return { type: "DELETE", deletedCount: ids.length };
40729
40875
  }
40730
40876
  async function executeUpsert(stmt, client, options, cacheContext) {
40877
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
40731
40878
  const toInsert = [];
40732
40879
  const toUpdate = [];
40733
40880
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
@@ -41154,6 +41301,7 @@ function evalOrderKeyForRow(key, row) {
41154
41301
  }
41155
41302
  }
41156
41303
  async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
41304
+ await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
41157
41305
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
41158
41306
  const { rows, columns } = selectResult;
41159
41307
  if (columns.length !== stmt.fields.length) {
@@ -41789,6 +41937,8 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
41789
41937
  }
41790
41938
  function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
41791
41939
  const isArith = hasArithAssignment(stmt);
41940
+ const isStringFunc = stmt.assignments.some((a) => a.value.type === "STRING_FUNC");
41941
+ const isRowDependent = hasRowDependentAssignment(stmt);
41792
41942
  const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
41793
41943
  const lines = [];
41794
41944
  if (label) lines.push(label);
@@ -41805,10 +41955,11 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
41805
41955
  lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
41806
41956
  const setTypes = [];
41807
41957
  if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
41958
+ if (isStringFunc) setTypes.push("\u6587\u5B57\u5217\u95A2\u6570 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A55\u4FA1\uFF09");
41808
41959
  if (isSubq) setTypes.push("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA SET");
41809
- if (!isArith && !isSubq) setTypes.push("\u5358\u7D14 SET");
41960
+ if (!isRowDependent && !isSubq) setTypes.push("\u5358\u7D14 SET");
41810
41961
  lines.push(` set type: ${setTypes.join(", ")}`);
41811
- if (isArith) {
41962
+ if (isRowDependent) {
41812
41963
  const refFields = collectArithRefFields(stmt);
41813
41964
  if (refFields.length > 0) {
41814
41965
  lines.push(` ref fields: ${refFields.join(", ")}\uFF08GET \u306B\u542B\u3081\u308B\uFF09`);
@@ -41894,6 +42045,7 @@ function collectArithRefFields(stmt) {
41894
42045
  const refs = /* @__PURE__ */ new Set();
41895
42046
  for (const { value } of stmt.assignments) {
41896
42047
  if (value.type === "ARITH") collectArithNodeRefs(value, refs);
42048
+ if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
41897
42049
  }
41898
42050
  return [...refs];
41899
42051
  }
@@ -41906,6 +42058,13 @@ function collectArithNodeRefs(node, out) {
41906
42058
  collectArithNodeRefs(node.left, out);
41907
42059
  collectArithNodeRefs(node.right, out);
41908
42060
  }
42061
+ if (node.type === "STRING_FUNC") {
42062
+ for (const arg of node.args) {
42063
+ if (arg.type !== "STRING" && arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") {
42064
+ collectArithNodeRefs(arg, out);
42065
+ }
42066
+ }
42067
+ }
41909
42068
  }
41910
42069
  function formatAssignment(a) {
41911
42070
  const v = a.value;
@@ -41913,6 +42072,7 @@ function formatAssignment(a) {
41913
42072
  if (v.type === "NUMBER") return `${a.field} = ${v.value}`;
41914
42073
  if (v.type === "ARITH") return `${a.field} = ${formatArithExprStr(v)}`;
41915
42074
  if (v.type === "CASE_VALUE") return `${a.field} = CASE WHEN ...`;
42075
+ if (v.type === "STRING_FUNC") return `${a.field} = ${v.func}(...)`;
41916
42076
  if (v.type === "SCALAR_SUBQUERY") return `${a.field} = (SELECT ...)`;
41917
42077
  if (v.type === "SOURCE_FIELD") return `${a.field} = ${v.alias}.${v.field}`;
41918
42078
  return `${a.field} = (${v.type})`;
@@ -44507,7 +44667,7 @@ Options:
44507
44667
  -h, --help Show help
44508
44668
  `);
44509
44669
  }
44510
- var SERVER_VERSION = true ? "3.1.0" : "0.0.0-dev";
44670
+ var SERVER_VERSION = true ? "3.2.0" : "0.0.0-dev";
44511
44671
  function createServer(args) {
44512
44672
  const server = new McpServer({
44513
44673
  name: "ksql-mcp",
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rex0220/kintone-sql-tools",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"