@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-cli/ksql.js
CHANGED
|
@@ -2054,6 +2054,26 @@ var Parser = class {
|
|
|
2054
2054
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
2055
2055
|
this.expect("SET" /* SET */);
|
|
2056
2056
|
const assignments = this.parseAssignments();
|
|
2057
|
+
let from = null;
|
|
2058
|
+
if (this.consume("FROM" /* FROM */)) {
|
|
2059
|
+
const table = this.parseTableRef();
|
|
2060
|
+
if (table.subtableCode) {
|
|
2061
|
+
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());
|
|
2062
|
+
}
|
|
2063
|
+
if (table.cteName !== null && !table.cteName.startsWith("#")) {
|
|
2064
|
+
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());
|
|
2065
|
+
}
|
|
2066
|
+
if (!table.alias) {
|
|
2067
|
+
throw new ParseError("UPDATE ... FROM \u306E\u30BD\u30FC\u30B9\u306B\u306F\u30A8\u30A4\u30EA\u30A2\u30B9\u304C\u5FC5\u8981\u3067\u3059", this.prev());
|
|
2068
|
+
}
|
|
2069
|
+
from = {
|
|
2070
|
+
appId: table.appId,
|
|
2071
|
+
cteName: table.cteName,
|
|
2072
|
+
alias: table.alias,
|
|
2073
|
+
joinKeyField: "",
|
|
2074
|
+
targetFilter: null
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2057
2077
|
const whereTok = this.peek();
|
|
2058
2078
|
if (!this.consume("WHERE" /* WHERE */)) {
|
|
2059
2079
|
throw new ParseError(
|
|
@@ -2062,8 +2082,129 @@ var Parser = class {
|
|
|
2062
2082
|
);
|
|
2063
2083
|
}
|
|
2064
2084
|
const where = this.parseWhereExpr();
|
|
2085
|
+
if (from !== null) {
|
|
2086
|
+
if (subtableCode) {
|
|
2087
|
+
throw new ParseError("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE ... FROM \u306F\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093", whereTok);
|
|
2088
|
+
}
|
|
2089
|
+
this.validateUpdateFromAssignments(assignments, from.alias, whereTok);
|
|
2090
|
+
const decomposed = this.decomposeUpdateFromWhere(where, appId, from.alias, whereTok);
|
|
2091
|
+
from.joinKeyField = decomposed.joinKeyField;
|
|
2092
|
+
from.targetFilter = decomposed.targetFilter;
|
|
2093
|
+
} else if (assignments.some((a) => a.value.type === "SOURCE_FIELD")) {
|
|
2094
|
+
throw new ParseError(
|
|
2095
|
+
"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",
|
|
2096
|
+
whereTok
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
if (from !== null) return { type: "UPDATE", appId, assignments, where, from };
|
|
2065
2100
|
return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where } : { type: "UPDATE", appId, assignments, where };
|
|
2066
2101
|
}
|
|
2102
|
+
validateUpdateFromAssignments(assignments, sourceAlias, tok) {
|
|
2103
|
+
for (const assignment of assignments) {
|
|
2104
|
+
if (assignment.value.type === "SOURCE_FIELD") {
|
|
2105
|
+
if (assignment.value.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
|
|
2106
|
+
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);
|
|
2107
|
+
}
|
|
2108
|
+
continue;
|
|
2109
|
+
}
|
|
2110
|
+
if (this.nodeContainsQualifiedField(assignment.value, sourceAlias)) {
|
|
2111
|
+
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);
|
|
2112
|
+
}
|
|
2113
|
+
if (assignment.value.type === "SCALAR_SUBQUERY") {
|
|
2114
|
+
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);
|
|
2115
|
+
}
|
|
2116
|
+
if (this.nodeContainsAnyQualifier(assignment.value)) {
|
|
2117
|
+
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);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
decomposeUpdateFromWhere(where, targetAppId, sourceAlias, tok) {
|
|
2122
|
+
const leaves = this.flattenTopLevelAnd(where);
|
|
2123
|
+
const joins = [];
|
|
2124
|
+
leaves.forEach((leaf, index) => {
|
|
2125
|
+
const sourceField = this.matchUpdateFromJoin(leaf, targetAppId, sourceAlias);
|
|
2126
|
+
if (sourceField !== null) joins.push({ index, sourceField });
|
|
2127
|
+
});
|
|
2128
|
+
if (joins.length !== 1) {
|
|
2129
|
+
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);
|
|
2130
|
+
}
|
|
2131
|
+
const join2 = joins[0];
|
|
2132
|
+
for (let i = 0; i < leaves.length; i++) {
|
|
2133
|
+
if (i !== join2.index && this.nodeContainsQualifiedField(leaves[i], sourceAlias)) {
|
|
2134
|
+
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);
|
|
2135
|
+
}
|
|
2136
|
+
if (i !== join2.index && this.nodeContainsForeignQualifier(leaves[i], targetAppId)) {
|
|
2137
|
+
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);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
const filters = leaves.filter((_, index) => index !== join2.index);
|
|
2141
|
+
const targetFilter = filters.reduce(
|
|
2142
|
+
(acc, expr) => acc === null ? expr : { type: "LOGICAL", op: "AND", left: acc, right: expr },
|
|
2143
|
+
null
|
|
2144
|
+
);
|
|
2145
|
+
return { joinKeyField: join2.sourceField, targetFilter };
|
|
2146
|
+
}
|
|
2147
|
+
flattenTopLevelAnd(expr) {
|
|
2148
|
+
if (expr.type === "GROUP") return this.flattenTopLevelAnd(expr.expr);
|
|
2149
|
+
if (expr.type === "LOGICAL" && expr.op === "AND") {
|
|
2150
|
+
return [...this.flattenTopLevelAnd(expr.left), ...this.flattenTopLevelAnd(expr.right)];
|
|
2151
|
+
}
|
|
2152
|
+
return [expr];
|
|
2153
|
+
}
|
|
2154
|
+
matchUpdateFromJoin(expr, targetAppId, sourceAlias) {
|
|
2155
|
+
if (expr.type !== "BINARY" || expr.op !== "=" || expr.left.type !== "FIELD") return null;
|
|
2156
|
+
const right = expr.right.type === "ARITH_VALUE" && expr.right.expr.type === "FIELD_REF" ? this.splitQualifiedField(expr.right.expr.field) : null;
|
|
2157
|
+
if (right === null) return null;
|
|
2158
|
+
const left = { alias: expr.left.tableAlias, field: expr.left.field };
|
|
2159
|
+
if (this.isTargetIdRef(left, targetAppId) && this.isSourceRef(right, sourceAlias)) return right.field;
|
|
2160
|
+
if (this.isSourceRef(left, sourceAlias) && this.isTargetIdRef(right, targetAppId)) return left.field;
|
|
2161
|
+
return null;
|
|
2162
|
+
}
|
|
2163
|
+
splitQualifiedField(field) {
|
|
2164
|
+
const dot = field.indexOf(".");
|
|
2165
|
+
return dot < 0 ? { alias: null, field } : { alias: field.slice(0, dot), field: field.slice(dot + 1) };
|
|
2166
|
+
}
|
|
2167
|
+
isTargetIdRef(ref, appId) {
|
|
2168
|
+
return ref.field === "$id" && (ref.alias === null || ref.alias.toLowerCase() === `app${appId}`.toLowerCase());
|
|
2169
|
+
}
|
|
2170
|
+
isSourceRef(ref, alias) {
|
|
2171
|
+
return ref.alias?.toLowerCase() === alias.toLowerCase();
|
|
2172
|
+
}
|
|
2173
|
+
nodeContainsQualifiedField(node, alias) {
|
|
2174
|
+
if (Array.isArray(node)) return node.some((v) => this.nodeContainsQualifiedField(v, alias));
|
|
2175
|
+
if (node === null || typeof node !== "object") return false;
|
|
2176
|
+
const obj = node;
|
|
2177
|
+
if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string" && obj["tableAlias"].toLowerCase() === alias.toLowerCase()) return true;
|
|
2178
|
+
if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
|
|
2179
|
+
const ref = this.splitQualifiedField(obj["field"]);
|
|
2180
|
+
if (this.isSourceRef(ref, alias)) return true;
|
|
2181
|
+
}
|
|
2182
|
+
return Object.values(obj).some((v) => this.nodeContainsQualifiedField(v, alias));
|
|
2183
|
+
}
|
|
2184
|
+
nodeContainsForeignQualifier(node, targetAppId) {
|
|
2185
|
+
if (Array.isArray(node)) return node.some((v) => this.nodeContainsForeignQualifier(v, targetAppId));
|
|
2186
|
+
if (node === null || typeof node !== "object") return false;
|
|
2187
|
+
const obj = node;
|
|
2188
|
+
const expected = `app${targetAppId}`.toLowerCase();
|
|
2189
|
+
if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string") {
|
|
2190
|
+
return obj["tableAlias"].toLowerCase() !== expected;
|
|
2191
|
+
}
|
|
2192
|
+
if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
|
|
2193
|
+
const ref = this.splitQualifiedField(obj["field"]);
|
|
2194
|
+
if (ref.alias !== null) return ref.alias.toLowerCase() !== expected;
|
|
2195
|
+
}
|
|
2196
|
+
return Object.values(obj).some((v) => this.nodeContainsForeignQualifier(v, targetAppId));
|
|
2197
|
+
}
|
|
2198
|
+
nodeContainsAnyQualifier(node) {
|
|
2199
|
+
if (Array.isArray(node)) return node.some((v) => this.nodeContainsAnyQualifier(v));
|
|
2200
|
+
if (node === null || typeof node !== "object") return false;
|
|
2201
|
+
const obj = node;
|
|
2202
|
+
if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string") return true;
|
|
2203
|
+
if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
|
|
2204
|
+
if (this.splitQualifiedField(obj["field"]).alias !== null) return true;
|
|
2205
|
+
}
|
|
2206
|
+
return Object.values(obj).some((v) => this.nodeContainsAnyQualifier(v));
|
|
2207
|
+
}
|
|
2067
2208
|
parseAssignments() {
|
|
2068
2209
|
const assignments = [];
|
|
2069
2210
|
do {
|
|
@@ -2098,6 +2239,12 @@ var Parser = class {
|
|
|
2098
2239
|
const node = this.parseArithAddSub();
|
|
2099
2240
|
if (node.type === "NUMBER") return node;
|
|
2100
2241
|
if (node.type === "ARITH") return node;
|
|
2242
|
+
if (node.type === "FIELD_REF") {
|
|
2243
|
+
const dot = node.field.indexOf(".");
|
|
2244
|
+
if (dot > 0 && dot < node.field.length - 1) {
|
|
2245
|
+
return { type: "SOURCE_FIELD", alias: node.field.slice(0, dot), field: node.field.slice(dot + 1) };
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2101
2248
|
throw new ParseError(
|
|
2102
2249
|
"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",
|
|
2103
2250
|
tok
|
|
@@ -3685,7 +3832,8 @@ function analyzeBatch(statements) {
|
|
|
3685
3832
|
tempTablesDropped: dropped,
|
|
3686
3833
|
dependsOn: [...dependsOn].sort((a, b) => a - b),
|
|
3687
3834
|
tempOnlySource,
|
|
3688
|
-
targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null
|
|
3835
|
+
targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null,
|
|
3836
|
+
isUpdateFrom: stmt.type === "UPDATE" && stmt.from != null
|
|
3689
3837
|
});
|
|
3690
3838
|
});
|
|
3691
3839
|
const containsDml = results.some((r) => r.isDml);
|
|
@@ -4230,7 +4378,7 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
|
4230
4378
|
function buildUpdateRecord(assignments, fieldTypes) {
|
|
4231
4379
|
const record = {};
|
|
4232
4380
|
for (const { field, value } of assignments) {
|
|
4233
|
-
if (value.type === "ARITH" || value.type === "CASE_VALUE") continue;
|
|
4381
|
+
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "SOURCE_FIELD") continue;
|
|
4234
4382
|
record[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
|
|
4235
4383
|
}
|
|
4236
4384
|
return record;
|
|
@@ -4334,6 +4482,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
|
|
|
4334
4482
|
record[field] = { value: String(evalArith(value, raw)) };
|
|
4335
4483
|
} else if (value.type === "CASE_VALUE") {
|
|
4336
4484
|
record[field] = { value: evalCaseWhenValue(value.expr, row, fieldTypes.get(field)) };
|
|
4485
|
+
} else if (value.type === "SOURCE_FIELD") {
|
|
4486
|
+
throw new DmlConvertError("SOURCE_FIELD \u306F UPDATE ... FROM \u5C02\u7528\u3067\u3059");
|
|
4337
4487
|
} else {
|
|
4338
4488
|
record[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
|
|
4339
4489
|
}
|
|
@@ -4345,6 +4495,51 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
|
|
|
4345
4495
|
records: batch
|
|
4346
4496
|
}));
|
|
4347
4497
|
}
|
|
4498
|
+
var UPDATE_FROM_UNSUPPORTED_TYPES = /* @__PURE__ */ new Set([
|
|
4499
|
+
"CHECK_BOX",
|
|
4500
|
+
"MULTI_SELECT",
|
|
4501
|
+
"USER_SELECT",
|
|
4502
|
+
"ORGANIZATION_SELECT",
|
|
4503
|
+
"GROUP_SELECT",
|
|
4504
|
+
"FILE"
|
|
4505
|
+
]);
|
|
4506
|
+
function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
4507
|
+
const updateRecords = matched.map(({ target, source }) => {
|
|
4508
|
+
const id = Number(target["$id"]?.value);
|
|
4509
|
+
if (!Number.isSafeInteger(id) || id <= 0) {
|
|
4510
|
+
throw new DmlConvertError("UPDATE ... FROM \u306E\u5BFE\u8C61\u30EC\u30B3\u30FC\u30C9\u756A\u53F7\u304C\u4E0D\u6B63\u3067\u3059");
|
|
4511
|
+
}
|
|
4512
|
+
const targetRow = kintoneRecordToProcessRow(target);
|
|
4513
|
+
const record = {};
|
|
4514
|
+
for (const { field, value } of stmt.assignments) {
|
|
4515
|
+
const fieldType = fieldTypes.get(field);
|
|
4516
|
+
if (value.type === "SOURCE_FIELD") {
|
|
4517
|
+
if (UPDATE_FROM_UNSUPPORTED_TYPES.has(fieldType ?? "")) {
|
|
4518
|
+
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`);
|
|
4519
|
+
}
|
|
4520
|
+
if (!Object.prototype.hasOwnProperty.call(source, value.field)) {
|
|
4521
|
+
throw new DmlConvertError(`UPDATE ... FROM \u306E\u30BD\u30FC\u30B9\u5217 ${value.field} \u304C\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
4522
|
+
}
|
|
4523
|
+
const raw = source[value.field];
|
|
4524
|
+
if (typeof raw !== "string") {
|
|
4525
|
+
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`);
|
|
4526
|
+
}
|
|
4527
|
+
if ((fieldType === "NUMBER" || fieldType === "CALC") && raw !== "" && !Number.isFinite(Number(raw))) {
|
|
4528
|
+
throw new DmlConvertError(`\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u306B\u5909\u63DB\u3067\u304D\u306A\u3044\u5024\u3067\u3059: ${raw}`);
|
|
4529
|
+
}
|
|
4530
|
+
record[field] = { value: toKintoneValue({ type: "STRING", value: raw }, fieldType) };
|
|
4531
|
+
} else if (value.type === "ARITH") {
|
|
4532
|
+
record[field] = { value: String(evalArith(value, target)) };
|
|
4533
|
+
} else if (value.type === "CASE_VALUE") {
|
|
4534
|
+
record[field] = { value: evalCaseWhenValue(value.expr, targetRow, fieldType) };
|
|
4535
|
+
} else {
|
|
4536
|
+
record[field] = { value: toKintoneValue(value, fieldType) };
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
return { id, record };
|
|
4540
|
+
});
|
|
4541
|
+
return chunk(updateRecords, 100).map((records) => ({ app: stmt.appId, records }));
|
|
4542
|
+
}
|
|
4348
4543
|
function kintoneRecordToProcessRow(raw) {
|
|
4349
4544
|
return Object.fromEntries(
|
|
4350
4545
|
Object.entries(raw).map(([k, v]) => [
|
|
@@ -4514,6 +4709,11 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
4514
4709
|
const parallel = Math.max(1, options.parallel ?? PARALLEL_DEFAULT);
|
|
4515
4710
|
const maxRecords = options.maxRecords ?? MAX_RECORDS_DEFAULT;
|
|
4516
4711
|
const onLimit = options.onLimit ?? "error";
|
|
4712
|
+
const stopAfter = options.stopAfter;
|
|
4713
|
+
if (stopAfter !== void 0 && (!Number.isSafeInteger(stopAfter) || stopAfter <= 0 || stopAfter > maxRecords)) {
|
|
4714
|
+
throw new RangeError("stopAfter must be a positive safe integer <= maxRecords");
|
|
4715
|
+
}
|
|
4716
|
+
const fetchCap = stopAfter ?? maxRecords;
|
|
4517
4717
|
const fetchFields = fields.length > 0 && !fields.includes("$id") ? [...fields, "$id"] : fields;
|
|
4518
4718
|
const allRecords = [];
|
|
4519
4719
|
let notified = false;
|
|
@@ -4524,6 +4724,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
4524
4724
|
const first = await fetchPage(fetcher, app, cursorQuery0, fetchFields, pageSize, windowOffset);
|
|
4525
4725
|
notifySearchAborted(first, options);
|
|
4526
4726
|
allRecords.push(...first.records);
|
|
4727
|
+
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
4728
|
+
return allRecords.slice(0, stopAfter);
|
|
4729
|
+
}
|
|
4527
4730
|
if (allRecords.length > maxRecords) {
|
|
4528
4731
|
if (onLimit === "truncate") {
|
|
4529
4732
|
if (!notified && options.onTruncate) {
|
|
@@ -4541,6 +4744,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
4541
4744
|
windowOffset = 0;
|
|
4542
4745
|
}
|
|
4543
4746
|
while (true) {
|
|
4747
|
+
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
4748
|
+
return allRecords.slice(0, stopAfter);
|
|
4749
|
+
}
|
|
4544
4750
|
if (allRecords.length >= maxRecords) {
|
|
4545
4751
|
if (onLimit === "truncate") {
|
|
4546
4752
|
if (!notified && options.onTruncate) {
|
|
@@ -4551,7 +4757,7 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
4551
4757
|
}
|
|
4552
4758
|
throw new FetchAllLimitError(limitMessage);
|
|
4553
4759
|
}
|
|
4554
|
-
const remaining =
|
|
4760
|
+
const remaining = fetchCap - allRecords.length;
|
|
4555
4761
|
const maxPagesByLimit = Math.ceil(remaining / pageSize);
|
|
4556
4762
|
const batchParallel = Math.max(1, Math.min(parallel, maxPagesByLimit));
|
|
4557
4763
|
const batchOffsets = [];
|
|
@@ -4570,6 +4776,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
4570
4776
|
let done = false;
|
|
4571
4777
|
for (const res of responses) {
|
|
4572
4778
|
allRecords.push(...res.records);
|
|
4779
|
+
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
4780
|
+
return allRecords.slice(0, stopAfter);
|
|
4781
|
+
}
|
|
4573
4782
|
if (allRecords.length > maxRecords) {
|
|
4574
4783
|
if (onLimit === "truncate") {
|
|
4575
4784
|
if (!notified && options.onTruncate) {
|
|
@@ -4995,10 +5204,10 @@ function applyLimit(rows, limit, offset) {
|
|
|
4995
5204
|
if (limit === null) return rows.slice(start);
|
|
4996
5205
|
return rows.slice(start, start + limit);
|
|
4997
5206
|
}
|
|
4998
|
-
function project(rows, columns, scalarCache, resolveFieldType) {
|
|
5207
|
+
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
4999
5208
|
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
5000
5209
|
const projected2 = rows.map((row) => stripParentShortcutColumns(row));
|
|
5001
|
-
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [];
|
|
5210
|
+
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
|
|
5002
5211
|
return { rows: projected2, columns: cols };
|
|
5003
5212
|
}
|
|
5004
5213
|
const defaultFieldKeys = buildDefaultFieldOutputKeys(columns);
|
|
@@ -5007,6 +5216,9 @@ function project(rows, columns, scalarCache, resolveFieldType) {
|
|
|
5007
5216
|
);
|
|
5008
5217
|
const outputKeys = hasWildcard ? null : computeOutputKeys(columns, defaultFieldKeys);
|
|
5009
5218
|
const orderedKeys = outputKeys ?? [];
|
|
5219
|
+
if (hasWildcard && rows.length === 0) {
|
|
5220
|
+
return { rows: [], columns: computeExplicitOutputKeys(columns, defaultFieldKeys) };
|
|
5221
|
+
}
|
|
5010
5222
|
const projected = rows.map((row, rowIdx) => {
|
|
5011
5223
|
const out = {};
|
|
5012
5224
|
for (const [colIdx, col] of columns.entries()) {
|
|
@@ -5084,29 +5296,43 @@ function project(rows, columns, scalarCache, resolveFieldType) {
|
|
|
5084
5296
|
return { rows: projected, columns: orderedKeys };
|
|
5085
5297
|
}
|
|
5086
5298
|
function computeOutputKeys(columns, defaultFieldKeys) {
|
|
5087
|
-
return columns.map((col, colIdx) =>
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
return col.alias ?? arithColDefaultKey(col.expr);
|
|
5099
|
-
case "CASE_COL":
|
|
5100
|
-
return col.alias ?? "case";
|
|
5101
|
-
case "STRFUNC_COL":
|
|
5102
|
-
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
5103
|
-
case "SCALAR_SUBQUERY_COL":
|
|
5104
|
-
return col.alias ?? "(subquery)";
|
|
5105
|
-
case "WILDCARD":
|
|
5106
|
-
case "PARENT_WILDCARD":
|
|
5107
|
-
throw new Error("internal: computeOutputKeys received a wildcard column");
|
|
5299
|
+
return columns.map((col, colIdx) => computeOutputKey(col, colIdx, defaultFieldKeys));
|
|
5300
|
+
}
|
|
5301
|
+
function computeExplicitOutputKeys(columns, defaultFieldKeys) {
|
|
5302
|
+
const keys = [];
|
|
5303
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5304
|
+
for (const [colIdx, col] of columns.entries()) {
|
|
5305
|
+
if (col.type === "WILDCARD" || col.type === "PARENT_WILDCARD") continue;
|
|
5306
|
+
const key = computeOutputKey(col, colIdx, defaultFieldKeys);
|
|
5307
|
+
if (!seen.has(key)) {
|
|
5308
|
+
seen.add(key);
|
|
5309
|
+
keys.push(key);
|
|
5108
5310
|
}
|
|
5109
|
-
}
|
|
5311
|
+
}
|
|
5312
|
+
return keys;
|
|
5313
|
+
}
|
|
5314
|
+
function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
5315
|
+
switch (col.type) {
|
|
5316
|
+
case "FIELD":
|
|
5317
|
+
return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
5318
|
+
case "LITERAL_COL":
|
|
5319
|
+
return col.alias ?? `'${col.value}'`;
|
|
5320
|
+
case "AGGREGATE":
|
|
5321
|
+
return col.alias ?? aggregateSyntheticName2(col.func, col.distinct, col.arg);
|
|
5322
|
+
case "ARITH_AGG_COL":
|
|
5323
|
+
return col.alias ?? aggArithDefaultKey(col.expr);
|
|
5324
|
+
case "ARITH_COL":
|
|
5325
|
+
return col.alias ?? arithColDefaultKey(col.expr);
|
|
5326
|
+
case "CASE_COL":
|
|
5327
|
+
return col.alias ?? "case";
|
|
5328
|
+
case "STRFUNC_COL":
|
|
5329
|
+
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
5330
|
+
case "SCALAR_SUBQUERY_COL":
|
|
5331
|
+
return col.alias ?? "(subquery)";
|
|
5332
|
+
case "WILDCARD":
|
|
5333
|
+
case "PARENT_WILDCARD":
|
|
5334
|
+
throw new Error("internal: computeOutputKey received a wildcard column");
|
|
5335
|
+
}
|
|
5110
5336
|
}
|
|
5111
5337
|
function buildDefaultFieldOutputKeys(columns) {
|
|
5112
5338
|
const qualifierCollisionCount = /* @__PURE__ */ new Map();
|
|
@@ -5200,7 +5426,8 @@ function runFullScan(input) {
|
|
|
5200
5426
|
sortKinds,
|
|
5201
5427
|
fieldTypeResolver,
|
|
5202
5428
|
havingFieldTypeResolver,
|
|
5203
|
-
appliedKlikes
|
|
5429
|
+
appliedKlikes,
|
|
5430
|
+
sourceColumns
|
|
5204
5431
|
} = input;
|
|
5205
5432
|
let rows = [];
|
|
5206
5433
|
const mainAlias = stmt.from.alias;
|
|
@@ -5222,7 +5449,7 @@ function runFullScan(input) {
|
|
|
5222
5449
|
}
|
|
5223
5450
|
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
|
|
5224
5451
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
5225
|
-
return project(rows, stmt.columns, scalarCache, fieldTypeResolver);
|
|
5452
|
+
return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
|
|
5226
5453
|
}
|
|
5227
5454
|
|
|
5228
5455
|
// src/converter/subtableAdapter.ts
|
|
@@ -5427,6 +5654,8 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
5427
5654
|
for (const s of analysis.statements) {
|
|
5428
5655
|
if (!s.isDml || s.tempTablesReferenced.length === 0) continue;
|
|
5429
5656
|
if (s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT") continue;
|
|
5657
|
+
const parsed = statements[s.index];
|
|
5658
|
+
if (parsed?.type === "UPDATE" && parsed.from?.cteName != null) continue;
|
|
5430
5659
|
throw new BatchAnalysisError(
|
|
5431
5660
|
`ArgumentError: temp table references in ${s.statementType} are not supported yet.`,
|
|
5432
5661
|
s.index
|
|
@@ -5556,7 +5785,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
5556
5785
|
onLimitReached: "error"
|
|
5557
5786
|
};
|
|
5558
5787
|
const result = await runSelectLike(resolvedStmt.query, client, materializeOptions, cacheContext, tempTables);
|
|
5559
|
-
tempTables.set(resolvedStmt.name, result.rows);
|
|
5788
|
+
tempTables.set(resolvedStmt.name, { rows: result.rows, columns: result.columns });
|
|
5560
5789
|
return { tempTable: resolvedStmt.name, rowCount: result.rows.length };
|
|
5561
5790
|
}
|
|
5562
5791
|
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
@@ -5583,6 +5812,9 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
5583
5812
|
if (resolvedStmt.type === "UPSERT_SELECT") {
|
|
5584
5813
|
return { result: await executeUpsertSelect(resolvedStmt, client, options, cacheContext, tempTables) };
|
|
5585
5814
|
}
|
|
5815
|
+
if (resolvedStmt.type === "UPDATE" && resolvedStmt.from?.cteName != null) {
|
|
5816
|
+
return { result: await executeUpdate(resolvedStmt, client, options, cacheContext, tempTables) };
|
|
5817
|
+
}
|
|
5586
5818
|
throw new Error(`ArgumentError: temp table references in ${stmt.type} are not supported yet.`);
|
|
5587
5819
|
}
|
|
5588
5820
|
return { result: await executeParsedStatement(resolvedStmt, client, options, cacheContext) };
|
|
@@ -5874,6 +6106,8 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
5874
6106
|
const onLimit = options.onLimitReached ?? "error";
|
|
5875
6107
|
const parallel = options.fetchParallel ?? 1;
|
|
5876
6108
|
const useSingleGet = stmt.limit !== null && stmt.limit <= 500;
|
|
6109
|
+
const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
|
|
6110
|
+
const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords && !whereHasKlike(stmt.where) ? needed : void 0;
|
|
5877
6111
|
let records;
|
|
5878
6112
|
if (useSingleGet) {
|
|
5879
6113
|
const res = await client.getRecords({
|
|
@@ -5892,6 +6126,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
5892
6126
|
{
|
|
5893
6127
|
parallel,
|
|
5894
6128
|
maxRecords,
|
|
6129
|
+
stopAfter,
|
|
5895
6130
|
onLimit,
|
|
5896
6131
|
onTruncate: (max) => {
|
|
5897
6132
|
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`);
|
|
@@ -6256,7 +6491,7 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
|
|
|
6256
6491
|
} else {
|
|
6257
6492
|
result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext);
|
|
6258
6493
|
}
|
|
6259
|
-
cteCache.set(cte.name, result.rows);
|
|
6494
|
+
cteCache.set(cte.name, { rows: result.rows, columns: result.columns });
|
|
6260
6495
|
}
|
|
6261
6496
|
return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
|
|
6262
6497
|
}
|
|
@@ -6309,8 +6544,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6309
6544
|
});
|
|
6310
6545
|
const tables = /* @__PURE__ */ new Map();
|
|
6311
6546
|
if (stmt.from.cteName != null) {
|
|
6312
|
-
const
|
|
6313
|
-
tables.set(stmt.from.alias,
|
|
6547
|
+
const table = cteCache.get(stmt.from.cteName);
|
|
6548
|
+
tables.set(stmt.from.alias, (table?.rows ?? []).map(processRowToKintoneRecord));
|
|
6314
6549
|
} else {
|
|
6315
6550
|
const mainRecords = await fetchTableRecordsForFullScan(
|
|
6316
6551
|
stmt,
|
|
@@ -6327,8 +6562,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6327
6562
|
}
|
|
6328
6563
|
const joinFetches = stmt.joins.map(async (join2) => {
|
|
6329
6564
|
if (join2.table.cteName != null) {
|
|
6330
|
-
const
|
|
6331
|
-
tables.set(join2.table.alias,
|
|
6565
|
+
const table = cteCache.get(join2.table.cteName);
|
|
6566
|
+
tables.set(join2.table.alias, (table?.rows ?? []).map(processRowToKintoneRecord));
|
|
6332
6567
|
} else {
|
|
6333
6568
|
const pushDownCond = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
6334
6569
|
const optimized = await tryFetchJoinRecordsBySourceKeys(
|
|
@@ -6359,6 +6594,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6359
6594
|
await Promise.all(joinFetches);
|
|
6360
6595
|
const scalarCache = await scalarCachePromise;
|
|
6361
6596
|
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
6597
|
+
const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
|
|
6362
6598
|
const { rows, columns } = runFullScan({
|
|
6363
6599
|
tables,
|
|
6364
6600
|
stmt,
|
|
@@ -6367,7 +6603,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6367
6603
|
sortKinds,
|
|
6368
6604
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
6369
6605
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
6370
|
-
appliedKlikes: pushdownPlan.appliedKlikes
|
|
6606
|
+
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
6607
|
+
sourceColumns
|
|
6371
6608
|
});
|
|
6372
6609
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
6373
6610
|
}
|
|
@@ -6763,10 +7000,13 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
6763
7000
|
insertedCount: createdIds.flat().length
|
|
6764
7001
|
};
|
|
6765
7002
|
}
|
|
6766
|
-
async function executeUpdate(stmt, client, options, cacheContext) {
|
|
7003
|
+
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
6767
7004
|
if (stmt.subtableCode) {
|
|
6768
7005
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
6769
7006
|
}
|
|
7007
|
+
if (stmt.from != null) {
|
|
7008
|
+
return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
7009
|
+
}
|
|
6770
7010
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
6771
7011
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
6772
7012
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
@@ -6808,6 +7048,113 @@ async function executeUpdate(stmt, client, options, cacheContext) {
|
|
|
6808
7048
|
}
|
|
6809
7049
|
return { type: "UPDATE", updatedCount: ids.length };
|
|
6810
7050
|
}
|
|
7051
|
+
var UPDATE_FROM_ID_CHUNK_SIZE = 50;
|
|
7052
|
+
var UPDATE_FROM_UNSUPPORTED_SOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
7053
|
+
"CHECK_BOX",
|
|
7054
|
+
"MULTI_SELECT",
|
|
7055
|
+
"USER_SELECT",
|
|
7056
|
+
"ORGANIZATION_SELECT",
|
|
7057
|
+
"GROUP_SELECT",
|
|
7058
|
+
"FILE"
|
|
7059
|
+
]);
|
|
7060
|
+
async function executeUpdateFrom(stmt, from, client, options, cacheContext, tempTables) {
|
|
7061
|
+
const sourceFields = [...new Set(stmt.assignments.filter((a) => a.value.type === "SOURCE_FIELD").map((a) => a.value.type === "SOURCE_FIELD" ? a.value.field : ""))];
|
|
7062
|
+
const requiredSourceFields = [.../* @__PURE__ */ new Set([from.joinKeyField, ...sourceFields])];
|
|
7063
|
+
let sourceRows;
|
|
7064
|
+
if (from.cteName !== null) {
|
|
7065
|
+
const table = tempTables?.get(from.cteName);
|
|
7066
|
+
if (!table) throw new Error(`ArgumentError: temp table ${from.cteName} is not available.`);
|
|
7067
|
+
for (const field of requiredSourceFields) {
|
|
7068
|
+
if (!table.columns.includes(field)) {
|
|
7069
|
+
throw new Error(`ArgumentError: UPDATE ... FROM source column ${field} does not exist.`);
|
|
7070
|
+
}
|
|
7071
|
+
}
|
|
7072
|
+
sourceRows = table.rows;
|
|
7073
|
+
} else {
|
|
7074
|
+
const sourceTypes = await getFieldTypeMap(from.appId, client, cacheContext);
|
|
7075
|
+
for (const field of requiredSourceFields) {
|
|
7076
|
+
if (field !== "$id" && !sourceTypes.has(field)) {
|
|
7077
|
+
throw new Error(`ArgumentError: UPDATE ... FROM source column ${field} does not exist.`);
|
|
7078
|
+
}
|
|
7079
|
+
const type = sourceTypes.get(field);
|
|
7080
|
+
if (UPDATE_FROM_UNSUPPORTED_SOURCE_TYPES.has(type ?? "")) {
|
|
7081
|
+
throw new Error(`ArgumentError: UPDATE ... FROM does not support source field type ${type} (${field}).`);
|
|
7082
|
+
}
|
|
7083
|
+
}
|
|
7084
|
+
const maxRecords = options.maxRecords ?? 1e4;
|
|
7085
|
+
const resolved = await fetchRecordsForSharedPlan(
|
|
7086
|
+
client.getRecords,
|
|
7087
|
+
from.appId,
|
|
7088
|
+
"",
|
|
7089
|
+
requiredSourceFields,
|
|
7090
|
+
{ maxRecords, parallel: options.fetchParallel ?? 1, onLimit: "error" }
|
|
7091
|
+
);
|
|
7092
|
+
sourceRows = resolved.records.map((record) => flatten(record, null));
|
|
7093
|
+
}
|
|
7094
|
+
const sourceById = /* @__PURE__ */ new Map();
|
|
7095
|
+
for (const row of sourceRows) {
|
|
7096
|
+
if (!Object.prototype.hasOwnProperty.call(row, from.joinKeyField)) {
|
|
7097
|
+
throw new Error(`ArgumentError: UPDATE ... FROM source column ${from.joinKeyField} does not exist.`);
|
|
7098
|
+
}
|
|
7099
|
+
const raw = row[from.joinKeyField];
|
|
7100
|
+
const text = typeof raw === "string" ? raw.trim() : "";
|
|
7101
|
+
const id = Number(text);
|
|
7102
|
+
if (text === "" || !Number.isSafeInteger(id) || id <= 0) {
|
|
7103
|
+
throw new Error(`ArgumentError: UPDATE ... FROM source key must be a positive safe integer: ${String(raw)}`);
|
|
7104
|
+
}
|
|
7105
|
+
if (sourceById.has(id)) {
|
|
7106
|
+
throw new Error(`ArgumentError: UPDATE ... FROM source has multiple rows for target $id ${id}.`);
|
|
7107
|
+
}
|
|
7108
|
+
sourceById.set(id, row);
|
|
7109
|
+
}
|
|
7110
|
+
const targetIds = [...sourceById.keys()];
|
|
7111
|
+
const targetFields = collectUpdateFromTargetFields(stmt);
|
|
7112
|
+
const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter }).query;
|
|
7113
|
+
const targetRecords = [];
|
|
7114
|
+
for (const ids of splitChunks(targetIds, UPDATE_FROM_ID_CHUNK_SIZE)) {
|
|
7115
|
+
const idQuery = `$id in (${ids.map((id) => sqlQuote(String(id))).join(",")})`;
|
|
7116
|
+
const query = filterQuery ? `(${idQuery}) and (${filterQuery})` : idQuery;
|
|
7117
|
+
const resolved = await fetchRecordsForSharedPlan(
|
|
7118
|
+
client.getRecords,
|
|
7119
|
+
stmt.appId,
|
|
7120
|
+
query,
|
|
7121
|
+
targetFields,
|
|
7122
|
+
{ maxRecords: Math.max(ids.length, 1), parallel: options.fetchParallel ?? 1, onLimit: "error" }
|
|
7123
|
+
);
|
|
7124
|
+
targetRecords.push(...resolved.records);
|
|
7125
|
+
}
|
|
7126
|
+
if (options.confirm) {
|
|
7127
|
+
const ok = await options.confirm(targetRecords.length, "UPDATE");
|
|
7128
|
+
if (!ok) throw new OperationCancelledError("UPDATE", targetRecords.length);
|
|
7129
|
+
}
|
|
7130
|
+
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
7131
|
+
const matched = targetRecords.map((target) => {
|
|
7132
|
+
const id = Number(target["$id"]?.value);
|
|
7133
|
+
const source = sourceById.get(id);
|
|
7134
|
+
if (!source) throw new Error(`ArgumentError: UPDATE ... FROM could not resolve source row for target $id ${id}.`);
|
|
7135
|
+
return { target, source };
|
|
7136
|
+
});
|
|
7137
|
+
const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
|
|
7138
|
+
for (const batch of batches) await client.putRecords(batch);
|
|
7139
|
+
return { type: "UPDATE", updatedCount: targetRecords.length };
|
|
7140
|
+
}
|
|
7141
|
+
function collectUpdateFromTargetFields(stmt) {
|
|
7142
|
+
const fields = /* @__PURE__ */ new Set(["$id"]);
|
|
7143
|
+
const visit = (node) => {
|
|
7144
|
+
if (Array.isArray(node)) {
|
|
7145
|
+
node.forEach(visit);
|
|
7146
|
+
return;
|
|
7147
|
+
}
|
|
7148
|
+
if (node === null || typeof node !== "object") return;
|
|
7149
|
+
const obj = node;
|
|
7150
|
+
if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") fields.add(obj["field"]);
|
|
7151
|
+
for (const value of Object.values(obj)) visit(value);
|
|
7152
|
+
};
|
|
7153
|
+
for (const assignment of stmt.assignments) {
|
|
7154
|
+
if (assignment.value.type !== "SOURCE_FIELD") visit(assignment.value);
|
|
7155
|
+
}
|
|
7156
|
+
return [...fields];
|
|
7157
|
+
}
|
|
6811
7158
|
async function executeDelete(stmt, client, options, cacheContext) {
|
|
6812
7159
|
if (stmt.subtableCode) {
|
|
6813
7160
|
return executeDeleteSubtable(stmt, client, options, cacheContext);
|
|
@@ -7733,9 +8080,16 @@ function buildUpdatePlan(stmt, label) {
|
|
|
7733
8080
|
const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
|
|
7734
8081
|
const lines = [];
|
|
7735
8082
|
if (label) lines.push(label);
|
|
7736
|
-
lines.push(` [UPDATE]`);
|
|
8083
|
+
lines.push(stmt.from ? ` [UPDATE FROM]` : ` [UPDATE]`);
|
|
7737
8084
|
lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
|
|
7738
|
-
|
|
8085
|
+
if (stmt.from) {
|
|
8086
|
+
const source = stmt.from.cteName ?? `APP${stmt.from.appId}`;
|
|
8087
|
+
lines.push(` source: ${source} AS ${stmt.from.alias}`);
|
|
8088
|
+
lines.push(` join: APP${stmt.appId}.$id = ${stmt.from.alias}.${stmt.from.joinKeyField}`);
|
|
8089
|
+
lines.push(` target filter: ${stmt.from.targetFilter ? safeWhereToKintone(stmt.from.targetFilter) : "(none)"}`);
|
|
8090
|
+
} else {
|
|
8091
|
+
lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
|
|
8092
|
+
}
|
|
7739
8093
|
lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
|
|
7740
8094
|
const setTypes = [];
|
|
7741
8095
|
if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
|
|
@@ -7848,6 +8202,7 @@ function formatAssignment(a) {
|
|
|
7848
8202
|
if (v.type === "ARITH") return `${a.field} = ${formatArithExprStr(v)}`;
|
|
7849
8203
|
if (v.type === "CASE_VALUE") return `${a.field} = CASE WHEN ...`;
|
|
7850
8204
|
if (v.type === "SCALAR_SUBQUERY") return `${a.field} = (SELECT ...)`;
|
|
8205
|
+
if (v.type === "SOURCE_FIELD") return `${a.field} = ${v.alias}.${v.field}`;
|
|
7851
8206
|
return `${a.field} = (${v.type})`;
|
|
7852
8207
|
}
|
|
7853
8208
|
function formatArithExprStr(expr) {
|
|
@@ -10333,6 +10688,11 @@ async function run() {
|
|
|
10333
10688
|
const yes = args.yes || envBool("KSQL_YES") === true || Boolean(profile.dml?.yes);
|
|
10334
10689
|
const allowWithoutWhere = args.allowWithoutWhere || envBool("KSQL_ALLOW_WITHOUT_WHERE") === true || Boolean(profile.dml?.allowWithoutWhere);
|
|
10335
10690
|
const dmlMaxRows = args.dmlMaxRows ?? envInt2("KSQL_DML_MAX_ROWS") ?? profile.dml?.maxRows ?? 100;
|
|
10691
|
+
const dmlForcesOnLimitError = isDmlStatement || batchContainsDml;
|
|
10692
|
+
const effectiveOnLimit = dmlForcesOnLimitError ? "error" : onLimit;
|
|
10693
|
+
if (dmlForcesOnLimitError && onLimit === "truncate" && !quiet && !args.dryRun) {
|
|
10694
|
+
process.stderr.write("note: onLimit=truncate is ignored for DML (forced to error)\n");
|
|
10695
|
+
}
|
|
10336
10696
|
if (format === "markdown" && noHeader) {
|
|
10337
10697
|
process.stderr.write("ArgumentError: --no-header cannot be used with --format markdown|md.\n");
|
|
10338
10698
|
return 2;
|
|
@@ -10684,7 +11044,7 @@ query=${label}`);
|
|
|
10684
11044
|
const batchResult = await executeBatch(sql, client, {
|
|
10685
11045
|
maxRecords,
|
|
10686
11046
|
fetchParallel,
|
|
10687
|
-
onLimitReached:
|
|
11047
|
+
onLimitReached: effectiveOnLimit,
|
|
10688
11048
|
cacheContext,
|
|
10689
11049
|
continueOnError: args.continueOnError,
|
|
10690
11050
|
tempTableMaxRows,
|
|
@@ -10702,7 +11062,7 @@ query=${label}`);
|
|
|
10702
11062
|
let result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, { maxRecords, onLimitReached: onLimit, cacheContext }) : await execute(sql, client, {
|
|
10703
11063
|
maxRecords,
|
|
10704
11064
|
fetchParallel,
|
|
10705
|
-
onLimitReached:
|
|
11065
|
+
onLimitReached: effectiveOnLimit,
|
|
10706
11066
|
confirm: isDmlStatement ? confirm : void 0,
|
|
10707
11067
|
cacheContext
|
|
10708
11068
|
});
|