@rex0220/kintone-sql-tools 2.10.1 → 2.12.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.
@@ -32967,6 +32967,26 @@ var Parser = class {
32967
32967
  const { appId, subtableCode } = extractTableRef(name, this.prev());
32968
32968
  this.expect("SET" /* SET */);
32969
32969
  const assignments = this.parseAssignments();
32970
+ let from = null;
32971
+ if (this.consume("FROM" /* FROM */)) {
32972
+ const table = this.parseTableRef();
32973
+ if (table.subtableCode) {
32974
+ throw new ParseError("UPDATE ... FROM \u306E\u30BD\u30FC\u30B9\u306B\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093", this.prev());
32975
+ }
32976
+ if (table.cteName !== null && !table.cteName.startsWith("#")) {
32977
+ throw new ParseError("UPDATE ... FROM \u306E\u30BD\u30FC\u30B9\u306F #temp \u307E\u305F\u306F APP<n> \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08CTE \u306F\u975E\u5BFE\u5FDC\uFF09", this.prev());
32978
+ }
32979
+ if (!table.alias) {
32980
+ throw new ParseError("UPDATE ... FROM \u306E\u30BD\u30FC\u30B9\u306B\u306F\u30A8\u30A4\u30EA\u30A2\u30B9\u304C\u5FC5\u8981\u3067\u3059", this.prev());
32981
+ }
32982
+ from = {
32983
+ appId: table.appId,
32984
+ cteName: table.cteName,
32985
+ alias: table.alias,
32986
+ joinKeyField: "",
32987
+ targetFilter: null
32988
+ };
32989
+ }
32970
32990
  const whereTok = this.peek();
32971
32991
  if (!this.consume("WHERE" /* WHERE */)) {
32972
32992
  throw new ParseError(
@@ -32975,8 +32995,129 @@ var Parser = class {
32975
32995
  );
32976
32996
  }
32977
32997
  const where = this.parseWhereExpr();
32998
+ if (from !== null) {
32999
+ if (subtableCode) {
33000
+ throw new ParseError("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE ... FROM \u306F\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093", whereTok);
33001
+ }
33002
+ this.validateUpdateFromAssignments(assignments, from.alias, whereTok);
33003
+ const decomposed = this.decomposeUpdateFromWhere(where, appId, from.alias, whereTok);
33004
+ from.joinKeyField = decomposed.joinKeyField;
33005
+ from.targetFilter = decomposed.targetFilter;
33006
+ } else if (assignments.some((a) => a.value.type === "SOURCE_FIELD")) {
33007
+ throw new ParseError(
33008
+ "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",
33009
+ whereTok
33010
+ );
33011
+ }
33012
+ if (from !== null) return { type: "UPDATE", appId, assignments, where, from };
32978
33013
  return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where } : { type: "UPDATE", appId, assignments, where };
32979
33014
  }
33015
+ validateUpdateFromAssignments(assignments, sourceAlias, tok) {
33016
+ for (const assignment of assignments) {
33017
+ if (assignment.value.type === "SOURCE_FIELD") {
33018
+ if (assignment.value.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
33019
+ 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);
33020
+ }
33021
+ continue;
33022
+ }
33023
+ if (this.nodeContainsQualifiedField(assignment.value, sourceAlias)) {
33024
+ throw new ParseError("UPDATE ... FROM \u306E\u30BD\u30FC\u30B9\u5217\u306F SET \u306E\u76F4\u63A5\u5024\u3068\u3057\u3066\u306E\u307F\u53C2\u7167\u3067\u304D\u307E\u3059", tok);
33025
+ }
33026
+ if (assignment.value.type === "SCALAR_SUBQUERY") {
33027
+ throw new ParseError("UPDATE ... FROM \u306E SET \u3067\u306F\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
33028
+ }
33029
+ if (this.nodeContainsAnyQualifier(assignment.value)) {
33030
+ throw new ParseError("UPDATE ... FROM \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u5F0F\u3067\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u4FEE\u98FE\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044", tok);
33031
+ }
33032
+ }
33033
+ }
33034
+ decomposeUpdateFromWhere(where, targetAppId, sourceAlias, tok) {
33035
+ const leaves = this.flattenTopLevelAnd(where);
33036
+ const joins = [];
33037
+ leaves.forEach((leaf, index) => {
33038
+ const sourceField = this.matchUpdateFromJoin(leaf, targetAppId, sourceAlias);
33039
+ if (sourceField !== null) joins.push({ index, sourceField });
33040
+ });
33041
+ if (joins.length !== 1) {
33042
+ throw new ParseError("UPDATE ... FROM \u306E WHERE \u306B\u306F target.$id = source.key \u306E\u7D50\u5408\u7B49\u5024\u304C\u3061\u3087\u3046\u30691\u3064\u5FC5\u8981\u3067\u3059", tok);
33043
+ }
33044
+ const join = joins[0];
33045
+ for (let i = 0; i < leaves.length; i++) {
33046
+ if (i !== join.index && this.nodeContainsQualifiedField(leaves[i], sourceAlias)) {
33047
+ throw new ParseError("UPDATE ... FROM \u306E\u30BD\u30FC\u30B9 alias \u306F\u7D50\u5408\u7B49\u5024\u4EE5\u5916\u306E WHERE \u6761\u4EF6\u3067\u306F\u53C2\u7167\u3067\u304D\u307E\u305B\u3093", tok);
33048
+ }
33049
+ if (i !== join.index && this.nodeContainsForeignQualifier(leaves[i], targetAppId)) {
33050
+ throw new ParseError(`UPDATE ... FROM \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u30D5\u30A3\u30EB\u30BF\u306F APP${targetAppId} \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u3060\u3051\u3092\u53C2\u7167\u3067\u304D\u307E\u3059`, tok);
33051
+ }
33052
+ }
33053
+ const filters = leaves.filter((_, index) => index !== join.index);
33054
+ const targetFilter = filters.reduce(
33055
+ (acc, expr) => acc === null ? expr : { type: "LOGICAL", op: "AND", left: acc, right: expr },
33056
+ null
33057
+ );
33058
+ return { joinKeyField: join.sourceField, targetFilter };
33059
+ }
33060
+ flattenTopLevelAnd(expr) {
33061
+ if (expr.type === "GROUP") return this.flattenTopLevelAnd(expr.expr);
33062
+ if (expr.type === "LOGICAL" && expr.op === "AND") {
33063
+ return [...this.flattenTopLevelAnd(expr.left), ...this.flattenTopLevelAnd(expr.right)];
33064
+ }
33065
+ return [expr];
33066
+ }
33067
+ matchUpdateFromJoin(expr, targetAppId, sourceAlias) {
33068
+ if (expr.type !== "BINARY" || expr.op !== "=" || expr.left.type !== "FIELD") return null;
33069
+ const right = expr.right.type === "ARITH_VALUE" && expr.right.expr.type === "FIELD_REF" ? this.splitQualifiedField(expr.right.expr.field) : null;
33070
+ if (right === null) return null;
33071
+ const left = { alias: expr.left.tableAlias, field: expr.left.field };
33072
+ if (this.isTargetIdRef(left, targetAppId) && this.isSourceRef(right, sourceAlias)) return right.field;
33073
+ if (this.isSourceRef(left, sourceAlias) && this.isTargetIdRef(right, targetAppId)) return left.field;
33074
+ return null;
33075
+ }
33076
+ splitQualifiedField(field) {
33077
+ const dot = field.indexOf(".");
33078
+ return dot < 0 ? { alias: null, field } : { alias: field.slice(0, dot), field: field.slice(dot + 1) };
33079
+ }
33080
+ isTargetIdRef(ref, appId) {
33081
+ return ref.field === "$id" && (ref.alias === null || ref.alias.toLowerCase() === `app${appId}`.toLowerCase());
33082
+ }
33083
+ isSourceRef(ref, alias) {
33084
+ return ref.alias?.toLowerCase() === alias.toLowerCase();
33085
+ }
33086
+ nodeContainsQualifiedField(node, alias) {
33087
+ if (Array.isArray(node)) return node.some((v) => this.nodeContainsQualifiedField(v, alias));
33088
+ if (node === null || typeof node !== "object") return false;
33089
+ const obj = node;
33090
+ if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string" && obj["tableAlias"].toLowerCase() === alias.toLowerCase()) return true;
33091
+ if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
33092
+ const ref = this.splitQualifiedField(obj["field"]);
33093
+ if (this.isSourceRef(ref, alias)) return true;
33094
+ }
33095
+ return Object.values(obj).some((v) => this.nodeContainsQualifiedField(v, alias));
33096
+ }
33097
+ nodeContainsForeignQualifier(node, targetAppId) {
33098
+ if (Array.isArray(node)) return node.some((v) => this.nodeContainsForeignQualifier(v, targetAppId));
33099
+ if (node === null || typeof node !== "object") return false;
33100
+ const obj = node;
33101
+ const expected = `app${targetAppId}`.toLowerCase();
33102
+ if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string") {
33103
+ return obj["tableAlias"].toLowerCase() !== expected;
33104
+ }
33105
+ if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
33106
+ const ref = this.splitQualifiedField(obj["field"]);
33107
+ if (ref.alias !== null) return ref.alias.toLowerCase() !== expected;
33108
+ }
33109
+ return Object.values(obj).some((v) => this.nodeContainsForeignQualifier(v, targetAppId));
33110
+ }
33111
+ nodeContainsAnyQualifier(node) {
33112
+ if (Array.isArray(node)) return node.some((v) => this.nodeContainsAnyQualifier(v));
33113
+ if (node === null || typeof node !== "object") return false;
33114
+ const obj = node;
33115
+ if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string") return true;
33116
+ if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
33117
+ if (this.splitQualifiedField(obj["field"]).alias !== null) return true;
33118
+ }
33119
+ return Object.values(obj).some((v) => this.nodeContainsAnyQualifier(v));
33120
+ }
32980
33121
  parseAssignments() {
32981
33122
  const assignments = [];
32982
33123
  do {
@@ -33011,6 +33152,12 @@ var Parser = class {
33011
33152
  const node = this.parseArithAddSub();
33012
33153
  if (node.type === "NUMBER") return node;
33013
33154
  if (node.type === "ARITH") return node;
33155
+ if (node.type === "FIELD_REF") {
33156
+ const dot = node.field.indexOf(".");
33157
+ if (dot > 0 && dot < node.field.length - 1) {
33158
+ return { type: "SOURCE_FIELD", alias: node.field.slice(0, dot), field: node.field.slice(dot + 1) };
33159
+ }
33160
+ }
33014
33161
  throw new ParseError(
33015
33162
  "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",
33016
33163
  tok
@@ -34586,7 +34733,8 @@ function analyzeBatch(statements) {
34586
34733
  tempTablesDropped: dropped,
34587
34734
  dependsOn: [...dependsOn].sort((a, b) => a - b),
34588
34735
  tempOnlySource,
34589
- targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null
34736
+ targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null,
34737
+ isUpdateFrom: stmt.type === "UPDATE" && stmt.from != null
34590
34738
  });
34591
34739
  });
34592
34740
  const containsDml = results.some((r) => r.isDml);
@@ -35131,7 +35279,7 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
35131
35279
  function buildUpdateRecord(assignments, fieldTypes) {
35132
35280
  const record2 = {};
35133
35281
  for (const { field, value } of assignments) {
35134
- if (value.type === "ARITH" || value.type === "CASE_VALUE") continue;
35282
+ if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "SOURCE_FIELD") continue;
35135
35283
  record2[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
35136
35284
  }
35137
35285
  return record2;
@@ -35235,6 +35383,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
35235
35383
  record2[field] = { value: String(evalArith(value, raw)) };
35236
35384
  } else if (value.type === "CASE_VALUE") {
35237
35385
  record2[field] = { value: evalCaseWhenValue(value.expr, row, fieldTypes.get(field)) };
35386
+ } else if (value.type === "SOURCE_FIELD") {
35387
+ throw new DmlConvertError("SOURCE_FIELD \u306F UPDATE ... FROM \u5C02\u7528\u3067\u3059");
35238
35388
  } else {
35239
35389
  record2[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
35240
35390
  }
@@ -35246,6 +35396,51 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
35246
35396
  records: batch
35247
35397
  }));
35248
35398
  }
35399
+ var UPDATE_FROM_UNSUPPORTED_TYPES = /* @__PURE__ */ new Set([
35400
+ "CHECK_BOX",
35401
+ "MULTI_SELECT",
35402
+ "USER_SELECT",
35403
+ "ORGANIZATION_SELECT",
35404
+ "GROUP_SELECT",
35405
+ "FILE"
35406
+ ]);
35407
+ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new Map()) {
35408
+ const updateRecords = matched.map(({ target, source }) => {
35409
+ const id = Number(target["$id"]?.value);
35410
+ if (!Number.isSafeInteger(id) || id <= 0) {
35411
+ throw new DmlConvertError("UPDATE ... FROM \u306E\u5BFE\u8C61\u30EC\u30B3\u30FC\u30C9\u756A\u53F7\u304C\u4E0D\u6B63\u3067\u3059");
35412
+ }
35413
+ const targetRow = kintoneRecordToProcessRow(target);
35414
+ const record2 = {};
35415
+ for (const { field, value } of stmt.assignments) {
35416
+ const fieldType = fieldTypes.get(field);
35417
+ if (value.type === "SOURCE_FIELD") {
35418
+ if (UPDATE_FROM_UNSUPPORTED_TYPES.has(fieldType ?? "")) {
35419
+ throw new DmlConvertError(`UPDATE ... FROM \u306E SOURCE_FIELD \u306F ${fieldType} \u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9: ${field}\uFF09`);
35420
+ }
35421
+ if (!Object.prototype.hasOwnProperty.call(source, value.field)) {
35422
+ throw new DmlConvertError(`UPDATE ... FROM \u306E\u30BD\u30FC\u30B9\u5217 ${value.field} \u304C\u5B58\u5728\u3057\u307E\u305B\u3093`);
35423
+ }
35424
+ const raw = source[value.field];
35425
+ if (typeof raw !== "string") {
35426
+ throw new DmlConvertError(`UPDATE ... FROM \u306E SOURCE_FIELD \u306F\u30B9\u30AB\u30E9\u30FC\u5024\u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\uFF08\u5217: ${value.field}\uFF09`);
35427
+ }
35428
+ if ((fieldType === "NUMBER" || fieldType === "CALC") && raw !== "" && !Number.isFinite(Number(raw))) {
35429
+ throw new DmlConvertError(`\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u306B\u5909\u63DB\u3067\u304D\u306A\u3044\u5024\u3067\u3059: ${raw}`);
35430
+ }
35431
+ record2[field] = { value: toKintoneValue({ type: "STRING", value: raw }, fieldType) };
35432
+ } else if (value.type === "ARITH") {
35433
+ record2[field] = { value: String(evalArith(value, target)) };
35434
+ } else if (value.type === "CASE_VALUE") {
35435
+ record2[field] = { value: evalCaseWhenValue(value.expr, targetRow, fieldType) };
35436
+ } else {
35437
+ record2[field] = { value: toKintoneValue(value, fieldType) };
35438
+ }
35439
+ }
35440
+ return { id, record: record2 };
35441
+ });
35442
+ return chunk(updateRecords, 100).map((records) => ({ app: stmt.appId, records }));
35443
+ }
35249
35444
  function kintoneRecordToProcessRow(raw) {
35250
35445
  return Object.fromEntries(
35251
35446
  Object.entries(raw).map(([k, v]) => [
@@ -35415,6 +35610,11 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35415
35610
  const parallel = Math.max(1, options.parallel ?? PARALLEL_DEFAULT);
35416
35611
  const maxRecords2 = options.maxRecords ?? MAX_RECORDS_DEFAULT;
35417
35612
  const onLimit2 = options.onLimit ?? "error";
35613
+ const stopAfter = options.stopAfter;
35614
+ if (stopAfter !== void 0 && (!Number.isSafeInteger(stopAfter) || stopAfter <= 0 || stopAfter > maxRecords2)) {
35615
+ throw new RangeError("stopAfter must be a positive safe integer <= maxRecords");
35616
+ }
35617
+ const fetchCap = stopAfter ?? maxRecords2;
35418
35618
  const fetchFields = fields.length > 0 && !fields.includes("$id") ? [...fields, "$id"] : fields;
35419
35619
  const allRecords = [];
35420
35620
  let notified = false;
@@ -35425,6 +35625,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35425
35625
  const first = await fetchPage(fetcher, app, cursorQuery0, fetchFields, pageSize, windowOffset);
35426
35626
  notifySearchAborted(first, options);
35427
35627
  allRecords.push(...first.records);
35628
+ if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
35629
+ return allRecords.slice(0, stopAfter);
35630
+ }
35428
35631
  if (allRecords.length > maxRecords2) {
35429
35632
  if (onLimit2 === "truncate") {
35430
35633
  if (!notified && options.onTruncate) {
@@ -35442,6 +35645,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35442
35645
  windowOffset = 0;
35443
35646
  }
35444
35647
  while (true) {
35648
+ if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
35649
+ return allRecords.slice(0, stopAfter);
35650
+ }
35445
35651
  if (allRecords.length >= maxRecords2) {
35446
35652
  if (onLimit2 === "truncate") {
35447
35653
  if (!notified && options.onTruncate) {
@@ -35452,7 +35658,7 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35452
35658
  }
35453
35659
  throw new FetchAllLimitError(limitMessage);
35454
35660
  }
35455
- const remaining = maxRecords2 - allRecords.length;
35661
+ const remaining = fetchCap - allRecords.length;
35456
35662
  const maxPagesByLimit = Math.ceil(remaining / pageSize);
35457
35663
  const batchParallel = Math.max(1, Math.min(parallel, maxPagesByLimit));
35458
35664
  const batchOffsets = [];
@@ -35471,6 +35677,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35471
35677
  let done = false;
35472
35678
  for (const res of responses) {
35473
35679
  allRecords.push(...res.records);
35680
+ if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
35681
+ return allRecords.slice(0, stopAfter);
35682
+ }
35474
35683
  if (allRecords.length > maxRecords2) {
35475
35684
  if (onLimit2 === "truncate") {
35476
35685
  if (!notified && options.onTruncate) {
@@ -35896,10 +36105,10 @@ function applyLimit(rows, limit, offset) {
35896
36105
  if (limit === null) return rows.slice(start);
35897
36106
  return rows.slice(start, start + limit);
35898
36107
  }
35899
- function project(rows, columns, scalarCache, resolveFieldType) {
36108
+ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
35900
36109
  if (columns.length === 1 && columns[0].type === "WILDCARD") {
35901
36110
  const projected2 = rows.map((row) => stripParentShortcutColumns(row));
35902
- const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [];
36111
+ const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
35903
36112
  return { rows: projected2, columns: cols };
35904
36113
  }
35905
36114
  const defaultFieldKeys = buildDefaultFieldOutputKeys(columns);
@@ -35908,6 +36117,9 @@ function project(rows, columns, scalarCache, resolveFieldType) {
35908
36117
  );
35909
36118
  const outputKeys = hasWildcard ? null : computeOutputKeys(columns, defaultFieldKeys);
35910
36119
  const orderedKeys = outputKeys ?? [];
36120
+ if (hasWildcard && rows.length === 0) {
36121
+ return { rows: [], columns: computeExplicitOutputKeys(columns, defaultFieldKeys) };
36122
+ }
35911
36123
  const projected = rows.map((row, rowIdx) => {
35912
36124
  const out = {};
35913
36125
  for (const [colIdx, col] of columns.entries()) {
@@ -35985,29 +36197,43 @@ function project(rows, columns, scalarCache, resolveFieldType) {
35985
36197
  return { rows: projected, columns: orderedKeys };
35986
36198
  }
35987
36199
  function computeOutputKeys(columns, defaultFieldKeys) {
35988
- return columns.map((col, colIdx) => {
35989
- switch (col.type) {
35990
- case "FIELD":
35991
- return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
35992
- case "LITERAL_COL":
35993
- return col.alias ?? `'${col.value}'`;
35994
- case "AGGREGATE":
35995
- return col.alias ?? aggregateSyntheticName2(col.func, col.distinct, col.arg);
35996
- case "ARITH_AGG_COL":
35997
- return col.alias ?? aggArithDefaultKey(col.expr);
35998
- case "ARITH_COL":
35999
- return col.alias ?? arithColDefaultKey(col.expr);
36000
- case "CASE_COL":
36001
- return col.alias ?? "case";
36002
- case "STRFUNC_COL":
36003
- return col.alias ?? stringFuncDefaultKey(col.expr);
36004
- case "SCALAR_SUBQUERY_COL":
36005
- return col.alias ?? "(subquery)";
36006
- case "WILDCARD":
36007
- case "PARENT_WILDCARD":
36008
- throw new Error("internal: computeOutputKeys received a wildcard column");
36200
+ return columns.map((col, colIdx) => computeOutputKey(col, colIdx, defaultFieldKeys));
36201
+ }
36202
+ function computeExplicitOutputKeys(columns, defaultFieldKeys) {
36203
+ const keys = [];
36204
+ const seen = /* @__PURE__ */ new Set();
36205
+ for (const [colIdx, col] of columns.entries()) {
36206
+ if (col.type === "WILDCARD" || col.type === "PARENT_WILDCARD") continue;
36207
+ const key = computeOutputKey(col, colIdx, defaultFieldKeys);
36208
+ if (!seen.has(key)) {
36209
+ seen.add(key);
36210
+ keys.push(key);
36009
36211
  }
36010
- });
36212
+ }
36213
+ return keys;
36214
+ }
36215
+ function computeOutputKey(col, colIdx, defaultFieldKeys) {
36216
+ switch (col.type) {
36217
+ case "FIELD":
36218
+ return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
36219
+ case "LITERAL_COL":
36220
+ return col.alias ?? `'${col.value}'`;
36221
+ case "AGGREGATE":
36222
+ return col.alias ?? aggregateSyntheticName2(col.func, col.distinct, col.arg);
36223
+ case "ARITH_AGG_COL":
36224
+ return col.alias ?? aggArithDefaultKey(col.expr);
36225
+ case "ARITH_COL":
36226
+ return col.alias ?? arithColDefaultKey(col.expr);
36227
+ case "CASE_COL":
36228
+ return col.alias ?? "case";
36229
+ case "STRFUNC_COL":
36230
+ return col.alias ?? stringFuncDefaultKey(col.expr);
36231
+ case "SCALAR_SUBQUERY_COL":
36232
+ return col.alias ?? "(subquery)";
36233
+ case "WILDCARD":
36234
+ case "PARENT_WILDCARD":
36235
+ throw new Error("internal: computeOutputKey received a wildcard column");
36236
+ }
36011
36237
  }
36012
36238
  function buildDefaultFieldOutputKeys(columns) {
36013
36239
  const qualifierCollisionCount = /* @__PURE__ */ new Map();
@@ -36101,7 +36327,8 @@ function runFullScan(input) {
36101
36327
  sortKinds,
36102
36328
  fieldTypeResolver,
36103
36329
  havingFieldTypeResolver,
36104
- appliedKlikes
36330
+ appliedKlikes,
36331
+ sourceColumns
36105
36332
  } = input;
36106
36333
  let rows = [];
36107
36334
  const mainAlias = stmt.from.alias;
@@ -36123,7 +36350,7 @@ function runFullScan(input) {
36123
36350
  }
36124
36351
  rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
36125
36352
  rows = applyLimit(rows, stmt.limit, stmt.offset);
36126
- return project(rows, stmt.columns, scalarCache, fieldTypeResolver);
36353
+ return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
36127
36354
  }
36128
36355
 
36129
36356
  // src/converter/subtableAdapter.ts
@@ -36328,6 +36555,8 @@ async function executeBatch(sql, client, options = {}) {
36328
36555
  for (const s of analysis.statements) {
36329
36556
  if (!s.isDml || s.tempTablesReferenced.length === 0) continue;
36330
36557
  if (s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT") continue;
36558
+ const parsed = statements[s.index];
36559
+ if (parsed?.type === "UPDATE" && parsed.from?.cteName != null) continue;
36331
36560
  throw new BatchAnalysisError(
36332
36561
  `ArgumentError: temp table references in ${s.statementType} are not supported yet.`,
36333
36562
  s.index
@@ -36457,7 +36686,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
36457
36686
  onLimitReached: "error"
36458
36687
  };
36459
36688
  const result = await runSelectLike(resolvedStmt.query, client, materializeOptions, cacheContext, tempTables);
36460
- tempTables.set(resolvedStmt.name, result.rows);
36689
+ tempTables.set(resolvedStmt.name, { rows: result.rows, columns: result.columns });
36461
36690
  return { tempTable: resolvedStmt.name, rowCount: result.rows.length };
36462
36691
  }
36463
36692
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -36484,6 +36713,9 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
36484
36713
  if (resolvedStmt.type === "UPSERT_SELECT") {
36485
36714
  return { result: await executeUpsertSelect(resolvedStmt, client, options, cacheContext, tempTables) };
36486
36715
  }
36716
+ if (resolvedStmt.type === "UPDATE" && resolvedStmt.from?.cteName != null) {
36717
+ return { result: await executeUpdate(resolvedStmt, client, options, cacheContext, tempTables) };
36718
+ }
36487
36719
  throw new Error(`ArgumentError: temp table references in ${stmt.type} are not supported yet.`);
36488
36720
  }
36489
36721
  return { result: await executeParsedStatement(resolvedStmt, client, options, cacheContext) };
@@ -36775,6 +37007,8 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
36775
37007
  const onLimit2 = options.onLimitReached ?? "error";
36776
37008
  const parallel = options.fetchParallel ?? 1;
36777
37009
  const useSingleGet = stmt.limit !== null && stmt.limit <= 500;
37010
+ const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
37011
+ const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords2 && !whereHasKlike(stmt.where) ? needed : void 0;
36778
37012
  let records;
36779
37013
  if (useSingleGet) {
36780
37014
  const res = await client.getRecords({
@@ -36793,6 +37027,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
36793
37027
  {
36794
37028
  parallel,
36795
37029
  maxRecords: maxRecords2,
37030
+ stopAfter,
36796
37031
  onLimit: onLimit2,
36797
37032
  onTruncate: (max) => {
36798
37033
  warnings.add(`\u53D6\u5F97\u4E0A\u9650\uFF08${max} \u4EF6\uFF09\u306B\u9054\u3057\u305F\u305F\u3081\u3001${max} \u4EF6\u3067\u6253\u3061\u5207\u3063\u3066\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002`);
@@ -37157,7 +37392,7 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
37157
37392
  } else {
37158
37393
  result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext);
37159
37394
  }
37160
- cteCache.set(cte.name, result.rows);
37395
+ cteCache.set(cte.name, { rows: result.rows, columns: result.columns });
37161
37396
  }
37162
37397
  return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
37163
37398
  }
@@ -37210,8 +37445,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37210
37445
  });
37211
37446
  const tables = /* @__PURE__ */ new Map();
37212
37447
  if (stmt.from.cteName != null) {
37213
- const rows2 = cteCache.get(stmt.from.cteName) ?? [];
37214
- tables.set(stmt.from.alias, rows2.map(processRowToKintoneRecord));
37448
+ const table = cteCache.get(stmt.from.cteName);
37449
+ tables.set(stmt.from.alias, (table?.rows ?? []).map(processRowToKintoneRecord));
37215
37450
  } else {
37216
37451
  const mainRecords = await fetchTableRecordsForFullScan(
37217
37452
  stmt,
@@ -37228,8 +37463,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37228
37463
  }
37229
37464
  const joinFetches = stmt.joins.map(async (join) => {
37230
37465
  if (join.table.cteName != null) {
37231
- const rows2 = cteCache.get(join.table.cteName) ?? [];
37232
- tables.set(join.table.alias, rows2.map(processRowToKintoneRecord));
37466
+ const table = cteCache.get(join.table.cteName);
37467
+ tables.set(join.table.alias, (table?.rows ?? []).map(processRowToKintoneRecord));
37233
37468
  } else {
37234
37469
  const pushDownCond = join.table.alias ? pushdownPlan.joinConditions.get(join.table.alias) ?? null : null;
37235
37470
  const optimized = await tryFetchJoinRecordsBySourceKeys(
@@ -37260,6 +37495,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37260
37495
  await Promise.all(joinFetches);
37261
37496
  const scalarCache = await scalarCachePromise;
37262
37497
  const { optionOrders, sortKinds } = await orderByMetaPromise;
37498
+ const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
37263
37499
  const { rows, columns } = runFullScan({
37264
37500
  tables,
37265
37501
  stmt,
@@ -37268,7 +37504,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37268
37504
  sortKinds,
37269
37505
  fieldTypeResolver: fieldTypeResolvers.row,
37270
37506
  havingFieldTypeResolver: fieldTypeResolvers.having,
37271
- appliedKlikes: pushdownPlan.appliedKlikes
37507
+ appliedKlikes: pushdownPlan.appliedKlikes,
37508
+ sourceColumns
37272
37509
  });
37273
37510
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
37274
37511
  }
@@ -37664,10 +37901,13 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
37664
37901
  insertedCount: createdIds.flat().length
37665
37902
  };
37666
37903
  }
37667
- async function executeUpdate(stmt, client, options, cacheContext) {
37904
+ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
37668
37905
  if (stmt.subtableCode) {
37669
37906
  return executeUpdateSubtable(stmt, client, options, cacheContext);
37670
37907
  }
37908
+ if (stmt.from != null) {
37909
+ return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
37910
+ }
37671
37911
  const maxRecords2 = options.maxRecords ?? 1e4;
37672
37912
  await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
37673
37913
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
@@ -37709,6 +37949,113 @@ async function executeUpdate(stmt, client, options, cacheContext) {
37709
37949
  }
37710
37950
  return { type: "UPDATE", updatedCount: ids.length };
37711
37951
  }
37952
+ var UPDATE_FROM_ID_CHUNK_SIZE = 50;
37953
+ var UPDATE_FROM_UNSUPPORTED_SOURCE_TYPES = /* @__PURE__ */ new Set([
37954
+ "CHECK_BOX",
37955
+ "MULTI_SELECT",
37956
+ "USER_SELECT",
37957
+ "ORGANIZATION_SELECT",
37958
+ "GROUP_SELECT",
37959
+ "FILE"
37960
+ ]);
37961
+ async function executeUpdateFrom(stmt, from, client, options, cacheContext, tempTables) {
37962
+ const sourceFields = [...new Set(stmt.assignments.filter((a) => a.value.type === "SOURCE_FIELD").map((a) => a.value.type === "SOURCE_FIELD" ? a.value.field : ""))];
37963
+ const requiredSourceFields = [.../* @__PURE__ */ new Set([from.joinKeyField, ...sourceFields])];
37964
+ let sourceRows;
37965
+ if (from.cteName !== null) {
37966
+ const table = tempTables?.get(from.cteName);
37967
+ if (!table) throw new Error(`ArgumentError: temp table ${from.cteName} is not available.`);
37968
+ for (const field of requiredSourceFields) {
37969
+ if (!table.columns.includes(field)) {
37970
+ throw new Error(`ArgumentError: UPDATE ... FROM source column ${field} does not exist.`);
37971
+ }
37972
+ }
37973
+ sourceRows = table.rows;
37974
+ } else {
37975
+ const sourceTypes = await getFieldTypeMap(from.appId, client, cacheContext);
37976
+ for (const field of requiredSourceFields) {
37977
+ if (field !== "$id" && !sourceTypes.has(field)) {
37978
+ throw new Error(`ArgumentError: UPDATE ... FROM source column ${field} does not exist.`);
37979
+ }
37980
+ const type = sourceTypes.get(field);
37981
+ if (UPDATE_FROM_UNSUPPORTED_SOURCE_TYPES.has(type ?? "")) {
37982
+ throw new Error(`ArgumentError: UPDATE ... FROM does not support source field type ${type} (${field}).`);
37983
+ }
37984
+ }
37985
+ const maxRecords2 = options.maxRecords ?? 1e4;
37986
+ const resolved = await fetchRecordsForSharedPlan(
37987
+ client.getRecords,
37988
+ from.appId,
37989
+ "",
37990
+ requiredSourceFields,
37991
+ { maxRecords: maxRecords2, parallel: options.fetchParallel ?? 1, onLimit: "error" }
37992
+ );
37993
+ sourceRows = resolved.records.map((record2) => flatten(record2, null));
37994
+ }
37995
+ const sourceById = /* @__PURE__ */ new Map();
37996
+ for (const row of sourceRows) {
37997
+ if (!Object.prototype.hasOwnProperty.call(row, from.joinKeyField)) {
37998
+ throw new Error(`ArgumentError: UPDATE ... FROM source column ${from.joinKeyField} does not exist.`);
37999
+ }
38000
+ const raw = row[from.joinKeyField];
38001
+ const text = typeof raw === "string" ? raw.trim() : "";
38002
+ const id = Number(text);
38003
+ if (text === "" || !Number.isSafeInteger(id) || id <= 0) {
38004
+ throw new Error(`ArgumentError: UPDATE ... FROM source key must be a positive safe integer: ${String(raw)}`);
38005
+ }
38006
+ if (sourceById.has(id)) {
38007
+ throw new Error(`ArgumentError: UPDATE ... FROM source has multiple rows for target $id ${id}.`);
38008
+ }
38009
+ sourceById.set(id, row);
38010
+ }
38011
+ const targetIds = [...sourceById.keys()];
38012
+ const targetFields = collectUpdateFromTargetFields(stmt);
38013
+ const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter }).query;
38014
+ const targetRecords = [];
38015
+ for (const ids of splitChunks(targetIds, UPDATE_FROM_ID_CHUNK_SIZE)) {
38016
+ const idQuery = `$id in (${ids.map((id) => sqlQuote(String(id))).join(",")})`;
38017
+ const query = filterQuery ? `(${idQuery}) and (${filterQuery})` : idQuery;
38018
+ const resolved = await fetchRecordsForSharedPlan(
38019
+ client.getRecords,
38020
+ stmt.appId,
38021
+ query,
38022
+ targetFields,
38023
+ { maxRecords: Math.max(ids.length, 1), parallel: options.fetchParallel ?? 1, onLimit: "error" }
38024
+ );
38025
+ targetRecords.push(...resolved.records);
38026
+ }
38027
+ if (options.confirm) {
38028
+ const ok = await options.confirm(targetRecords.length, "UPDATE");
38029
+ if (!ok) throw new OperationCancelledError("UPDATE", targetRecords.length);
38030
+ }
38031
+ const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
38032
+ const matched = targetRecords.map((target) => {
38033
+ const id = Number(target["$id"]?.value);
38034
+ const source = sourceById.get(id);
38035
+ if (!source) throw new Error(`ArgumentError: UPDATE ... FROM could not resolve source row for target $id ${id}.`);
38036
+ return { target, source };
38037
+ });
38038
+ const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
38039
+ for (const batch of batches) await client.putRecords(batch);
38040
+ return { type: "UPDATE", updatedCount: targetRecords.length };
38041
+ }
38042
+ function collectUpdateFromTargetFields(stmt) {
38043
+ const fields = /* @__PURE__ */ new Set(["$id"]);
38044
+ const visit = (node) => {
38045
+ if (Array.isArray(node)) {
38046
+ node.forEach(visit);
38047
+ return;
38048
+ }
38049
+ if (node === null || typeof node !== "object") return;
38050
+ const obj = node;
38051
+ if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") fields.add(obj["field"]);
38052
+ for (const value of Object.values(obj)) visit(value);
38053
+ };
38054
+ for (const assignment of stmt.assignments) {
38055
+ if (assignment.value.type !== "SOURCE_FIELD") visit(assignment.value);
38056
+ }
38057
+ return [...fields];
38058
+ }
37712
38059
  async function executeDelete(stmt, client, options, cacheContext) {
37713
38060
  if (stmt.subtableCode) {
37714
38061
  return executeDeleteSubtable(stmt, client, options, cacheContext);
@@ -38634,9 +38981,16 @@ function buildUpdatePlan(stmt, label) {
38634
38981
  const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
38635
38982
  const lines = [];
38636
38983
  if (label) lines.push(label);
38637
- lines.push(` [UPDATE]`);
38984
+ lines.push(stmt.from ? ` [UPDATE FROM]` : ` [UPDATE]`);
38638
38985
  lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
38639
- lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
38986
+ if (stmt.from) {
38987
+ const source = stmt.from.cteName ?? `APP${stmt.from.appId}`;
38988
+ lines.push(` source: ${source} AS ${stmt.from.alias}`);
38989
+ lines.push(` join: APP${stmt.appId}.$id = ${stmt.from.alias}.${stmt.from.joinKeyField}`);
38990
+ lines.push(` target filter: ${stmt.from.targetFilter ? safeWhereToKintone(stmt.from.targetFilter) : "(none)"}`);
38991
+ } else {
38992
+ lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
38993
+ }
38640
38994
  lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
38641
38995
  const setTypes = [];
38642
38996
  if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
@@ -38749,6 +39103,7 @@ function formatAssignment(a) {
38749
39103
  if (v.type === "ARITH") return `${a.field} = ${formatArithExprStr(v)}`;
38750
39104
  if (v.type === "CASE_VALUE") return `${a.field} = CASE WHEN ...`;
38751
39105
  if (v.type === "SCALAR_SUBQUERY") return `${a.field} = (SELECT ...)`;
39106
+ if (v.type === "SOURCE_FIELD") return `${a.field} = ${v.alias}.${v.field}`;
38752
39107
  return `${a.field} = (${v.type})`;
38753
39108
  }
38754
39109
  function formatArithExprStr(expr) {
@@ -40282,14 +40637,14 @@ function requireDmlApproval(input, toolName, suffix = "") {
40282
40637
  }
40283
40638
  function containsSelectBasedDml(statements) {
40284
40639
  return statements.some(
40285
- (s) => s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT"
40640
+ (s) => s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT" || s.isUpdateFrom === true
40286
40641
  );
40287
40642
  }
40288
40643
  function resolveMutateRuntimeMaxRecords(statements, dmlMaxRows) {
40289
40644
  return containsSelectBasedDml(statements) ? void 0 : dmlMaxRows + 1;
40290
40645
  }
40291
40646
  var READ_LIMIT_MESSAGE_FRAGMENT = "\u53D6\u5F97\u4EF6\u6570\u304C\u4E0A\u9650";
40292
- var SELECT_BASED_DML_READ_LIMIT_HINT = "SELECT-based DML \u306E\u30BD\u30FC\u30B9\u8AAD\u307F\u53D6\u308A\u4E0A\u9650\u306F dmlMaxRows \u3067\u306F\u306A\u304F maxRecords \u89E3\u6C7A\u5024(KSQL_MAX_RECORDS / profile \u306E query.maxRecords\u3001\u65E2\u5B9A 500)\u3067\u5236\u5FA1\u3055\u308C\u307E\u3059\u3002dmlMaxRows \u306F\u5F71\u97FF\u884C\u6570\u30AC\u30FC\u30C9\u3067\u3059\u3002";
40647
+ var SELECT_BASED_DML_READ_LIMIT_HINT = "SELECT-based DML\uFF08UPDATE \u2026 FROM \u3092\u542B\u3080\uFF09\u306E\u30BD\u30FC\u30B9\u8AAD\u307F\u53D6\u308A\u4E0A\u9650\u306F dmlMaxRows \u3067\u306F\u306A\u304F maxRecords \u89E3\u6C7A\u5024(KSQL_MAX_RECORDS / profile \u306E query.maxRecords\u3001\u65E2\u5B9A 500)\u3067\u5236\u5FA1\u3055\u308C\u307E\u3059\u3002dmlMaxRows \u306F\u5F71\u97FF\u884C\u6570\u30AC\u30FC\u30C9\u3067\u3059\u3002";
40293
40648
  function appendSelectBasedDmlReadLimitHint(err) {
40294
40649
  if (err instanceof Error && err.message.includes(READ_LIMIT_MESSAGE_FRAGMENT)) {
40295
40650
  const hinted = new Error(`${err.message} ${SELECT_BASED_DML_READ_LIMIT_HINT}`);
@@ -40344,7 +40699,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
40344
40699
  tempTablesReferenced: s2.tempTablesReferenced,
40345
40700
  tempTablesDropped: s2.tempTablesDropped,
40346
40701
  tempOnlySource: s2.tempOnlySource,
40347
- targetAppId: s2.targetAppId
40702
+ targetAppId: s2.targetAppId,
40703
+ isUpdateFrom: s2.isUpdateFrom
40348
40704
  }));
40349
40705
  const common = {
40350
40706
  ok: true,
@@ -40547,7 +40903,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
40547
40903
  const payload = buildBatchEnvelope(batchResult);
40548
40904
  if (selectBasedDml) {
40549
40905
  for (const entry of payload.statements) {
40550
- if (entry.type !== "INSERT_SELECT" && entry.type !== "UPSERT_SELECT") continue;
40906
+ const statement = validation.statements.find((s) => s.index === entry.index);
40907
+ if (entry.type !== "INSERT_SELECT" && entry.type !== "UPSERT_SELECT" && statement?.isUpdateFrom !== true) continue;
40551
40908
  const error51 = entry.error;
40552
40909
  if (typeof error51?.message !== "string") continue;
40553
40910
  if (!error51.message.includes(READ_LIMIT_MESSAGE_FRAGMENT)) continue;
@@ -40886,7 +41243,7 @@ Options:
40886
41243
  -h, --help Show help
40887
41244
  `);
40888
41245
  }
40889
- var SERVER_VERSION = true ? "2.10.1" : "0.0.0-dev";
41246
+ var SERVER_VERSION = true ? "2.12.0" : "0.0.0-dev";
40890
41247
  function createServer(args) {
40891
41248
  const server = new McpServer({
40892
41249
  name: "ksql-mcp",
@@ -40913,7 +41270,7 @@ function createServer(args) {
40913
41270
  }, tools.queryTool);
40914
41271
  server.registerTool("ksql_mutate", {
40915
41272
  title: "Run mutating kSQL",
40916
- description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: the source SELECT reads up to the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows by default (adjustable via tempTableMaxRows).",
41273
+ description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. UPDATE ... FROM supports copying scalar fields from an app or temp table by matching target $id to one source key. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: source SELECT and UPDATE ... FROM app reads use the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows by default (adjustable via tempTableMaxRows).",
40917
41274
  inputSchema: mutateInputShape
40918
41275
  }, tools.mutateTool);
40919
41276
  server.registerTool("ksql_describe_app", {