@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.
- package/README.md +1 -1
- package/dist-cli/ksql.js +401 -41
- package/dist-mcp/ksql-mcp.js +402 -45
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
35998
|
-
|
|
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
|
|
37214
|
-
tables.set(stmt.from.alias,
|
|
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
|
|
37232
|
-
tables.set(join.table.alias,
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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", {
|