@rex0220/kintone-sql-tools 3.9.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cli/ksql.js +282 -63
- package/dist-mcp/ksql-mcp.js +286 -65
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -4020,18 +4020,18 @@ function assertSafeParentWhere(where, multipleParents) {
|
|
|
4020
4020
|
if (!multipleParents) {
|
|
4021
4021
|
unsupported("a parent WHERE other than the single condition $id = <positive safe integer> in this phase");
|
|
4022
4022
|
}
|
|
4023
|
-
assertSafeParentPredicateNode(where);
|
|
4023
|
+
assertSafeParentPredicateNode(where, multipleParents);
|
|
4024
4024
|
}
|
|
4025
|
-
function assertSafeParentPredicateNode(node) {
|
|
4025
|
+
function assertSafeParentPredicateNode(node, allowKlike) {
|
|
4026
4026
|
if (Array.isArray(node)) {
|
|
4027
|
-
for (const value of node) assertSafeParentPredicateNode(value);
|
|
4027
|
+
for (const value of node) assertSafeParentPredicateNode(value, allowKlike);
|
|
4028
4028
|
return;
|
|
4029
4029
|
}
|
|
4030
4030
|
if (node === null || typeof node !== "object") return;
|
|
4031
4031
|
const item = node;
|
|
4032
4032
|
const type = typeof item["type"] === "string" ? item["type"] : null;
|
|
4033
4033
|
const op = typeof item["op"] === "string" ? item["op"] : null;
|
|
4034
|
-
if (op === "KLIKE" || op === "NOT_KLIKE") unsupported("KLIKE in parent WHERE");
|
|
4034
|
+
if (!allowKlike && (op === "KLIKE" || op === "NOT_KLIKE")) unsupported("KLIKE in parent WHERE");
|
|
4035
4035
|
if (type === "SELECT" || type === "SCALAR_SUBQUERY" || type === "SUBQUERY_IN_LIST" || type === "EXISTS") {
|
|
4036
4036
|
unsupported("subqueries in parent WHERE");
|
|
4037
4037
|
}
|
|
@@ -4040,7 +4040,7 @@ function assertSafeParentPredicateNode(node) {
|
|
|
4040
4040
|
unsupported("aggregate or window expressions in parent WHERE");
|
|
4041
4041
|
}
|
|
4042
4042
|
if (type === "KINTONE_FUNC") unsupported("non-deterministic kintone functions in parent WHERE");
|
|
4043
|
-
for (const value of Object.values(item)) assertSafeParentPredicateNode(value);
|
|
4043
|
+
for (const value of Object.values(item)) assertSafeParentPredicateNode(value, allowKlike);
|
|
4044
4044
|
}
|
|
4045
4045
|
function assertSafeChildPredicate(where, idxSelectors) {
|
|
4046
4046
|
assertSafeApplyNode(where, "child row selectors", idxSelectors);
|
|
@@ -5082,31 +5082,31 @@ function isTargetField(field, options) {
|
|
|
5082
5082
|
}
|
|
5083
5083
|
|
|
5084
5084
|
// src/core/optimization/klikePushdownPlan.ts
|
|
5085
|
+
function buildSingleTableKlikePushdownPlan(where, options = {}) {
|
|
5086
|
+
const condition = where !== null && options.extractCondition !== false ? extractSafePushdownLeaves(where, options) : null;
|
|
5087
|
+
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
5088
|
+
collectKlikes(condition, appliedKlikes);
|
|
5089
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
5090
|
+
collectKlikes(where, allKlikes);
|
|
5091
|
+
return { condition, appliedKlikes, allKlikes: [...allKlikes] };
|
|
5092
|
+
}
|
|
5085
5093
|
function buildKlikePushdownPlan(stmt, options = {}) {
|
|
5086
5094
|
const joinsAreSafeForKlike = stmt.joins.every((join2) => join2.type === "INNER");
|
|
5087
5095
|
const common = {
|
|
5088
5096
|
allowKlike: joinsAreSafeForKlike,
|
|
5089
5097
|
allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
|
|
5090
5098
|
};
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
mainCondition = extractSafePushdownLeaves(stmt.where, {
|
|
5103
|
-
...common,
|
|
5104
|
-
tableAlias: stmt.from.alias,
|
|
5105
|
-
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
5106
|
-
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
5107
|
-
});
|
|
5108
|
-
}
|
|
5109
|
-
}
|
|
5099
|
+
const mainIsPhysical = !stmt.from.subtableCode && stmt.from.cteName === null;
|
|
5100
|
+
const mainHasUsableAlias = stmt.joins.length === 0 || stmt.from.alias !== null;
|
|
5101
|
+
const mainPlan = buildSingleTableKlikePushdownPlan(stmt.where, {
|
|
5102
|
+
...common,
|
|
5103
|
+
extractCondition: mainIsPhysical && mainHasUsableAlias,
|
|
5104
|
+
tableAlias: stmt.from.alias ?? void 0,
|
|
5105
|
+
allowUnqualifiedFields: stmt.joins.length === 0,
|
|
5106
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
5107
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
5108
|
+
});
|
|
5109
|
+
const mainCondition = mainPlan.condition;
|
|
5110
5110
|
const joinConditions = /* @__PURE__ */ new Map();
|
|
5111
5111
|
if (stmt.where !== null) {
|
|
5112
5112
|
for (const join2 of stmt.joins) {
|
|
@@ -5120,16 +5120,13 @@ function buildKlikePushdownPlan(stmt, options = {}) {
|
|
|
5120
5120
|
if (condition !== null) joinConditions.set(join2.table.alias, condition);
|
|
5121
5121
|
}
|
|
5122
5122
|
}
|
|
5123
|
-
const appliedKlikes =
|
|
5124
|
-
collectKlikes(mainCondition, appliedKlikes);
|
|
5123
|
+
const appliedKlikes = new Set(mainPlan.appliedKlikes);
|
|
5125
5124
|
for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
|
|
5126
|
-
const allKlikes = /* @__PURE__ */ new Set();
|
|
5127
|
-
collectKlikes(stmt.where, allKlikes);
|
|
5128
5125
|
return {
|
|
5129
5126
|
mainCondition,
|
|
5130
5127
|
joinConditions,
|
|
5131
5128
|
appliedKlikes,
|
|
5132
|
-
allKlikes:
|
|
5129
|
+
allKlikes: mainPlan.allKlikes
|
|
5133
5130
|
};
|
|
5134
5131
|
}
|
|
5135
5132
|
function unappliedKlikes(plan) {
|
|
@@ -5197,16 +5194,65 @@ function validateStatement(stmt) {
|
|
|
5197
5194
|
case "ASSERT":
|
|
5198
5195
|
validateNestedSelects(stmt);
|
|
5199
5196
|
return;
|
|
5197
|
+
case "UPDATE":
|
|
5198
|
+
if (stmt.applyBlocks?.length && !isSinglePositiveRecordIdWhere(stmt.where) && whereHasKlike(stmt.where)) {
|
|
5199
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
5200
|
+
validateNestedSelects(stmt);
|
|
5201
|
+
return;
|
|
5202
|
+
}
|
|
5203
|
+
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
5204
|
+
throw new KlikeValidationError(
|
|
5205
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5206
|
+
);
|
|
5207
|
+
}
|
|
5208
|
+
if (!stmt.applyBlocks?.length && !stmt.subtableCode && whereHasKlike(stmt.where) && !containsKlikeOutsideWhereAndNestedSelects(stmt, stmt.where)) {
|
|
5209
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
5210
|
+
validateNestedSelects(stmt);
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
if (containsKlike(stmt)) {
|
|
5214
|
+
throw new KlikeValidationError(
|
|
5215
|
+
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA UPDATE \u306E WHERE\u3001\u307E\u305F\u306F APPLY \u8907\u6570\u89AA UPDATE \u306E\u5B89\u5168\u306A\u89AA WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
5216
|
+
);
|
|
5217
|
+
}
|
|
5218
|
+
return;
|
|
5219
|
+
case "DELETE":
|
|
5220
|
+
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
5221
|
+
throw new KlikeValidationError(
|
|
5222
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB DELETE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5223
|
+
);
|
|
5224
|
+
}
|
|
5225
|
+
if (!stmt.subtableCode && whereHasKlike(stmt.where)) {
|
|
5226
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
5227
|
+
validateNestedSelects(stmt);
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
if (containsKlike(stmt)) {
|
|
5231
|
+
throw new KlikeValidationError(
|
|
5232
|
+
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA DELETE \u306E WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
5233
|
+
);
|
|
5234
|
+
}
|
|
5235
|
+
return;
|
|
5200
5236
|
case "INSERT":
|
|
5201
5237
|
case "INSERT_SELECT":
|
|
5238
|
+
if (containsKlike(stmt)) {
|
|
5239
|
+
throw new KlikeValidationError(
|
|
5240
|
+
"KLIKE / NOT KLIKE \u306F INSERT / INSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5241
|
+
);
|
|
5242
|
+
}
|
|
5243
|
+
return;
|
|
5202
5244
|
case "UPSERT":
|
|
5203
5245
|
case "UPSERT_SELECT":
|
|
5204
|
-
|
|
5205
|
-
|
|
5246
|
+
if (containsKlike(stmt)) {
|
|
5247
|
+
throw new KlikeValidationError(
|
|
5248
|
+
"KLIKE / NOT KLIKE \u306F UPSERT / UPSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5249
|
+
);
|
|
5250
|
+
}
|
|
5251
|
+
return;
|
|
5206
5252
|
case "REORDER":
|
|
5207
5253
|
if (containsKlike(stmt)) {
|
|
5208
5254
|
throw new KlikeValidationError(
|
|
5209
|
-
"KLIKE / NOT KLIKE \u306F\
|
|
5255
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB REORDER \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5210
5256
|
);
|
|
5211
5257
|
}
|
|
5212
5258
|
return;
|
|
@@ -5221,6 +5267,22 @@ function validateStatement(stmt) {
|
|
|
5221
5267
|
return;
|
|
5222
5268
|
}
|
|
5223
5269
|
}
|
|
5270
|
+
function validateKlikeWhereExpressions(where) {
|
|
5271
|
+
walkWithoutNestedSelects(where, (expr) => {
|
|
5272
|
+
if (!isKlike(expr)) return;
|
|
5273
|
+
const right = expr.right;
|
|
5274
|
+
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
5275
|
+
throw new KlikeValidationError(
|
|
5276
|
+
"KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u6587\u5B57\u5217\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
5277
|
+
);
|
|
5278
|
+
}
|
|
5279
|
+
if (right.type === "STRING" && right.value.includes("%")) {
|
|
5280
|
+
throw new KlikeValidationError(
|
|
5281
|
+
"KLIKE / NOT KLIKE \u306E\u691C\u7D22\u8A9E\u306B % \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002SQL \u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u691C\u7D22\u306B\u306F LIKE \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
5282
|
+
);
|
|
5283
|
+
}
|
|
5284
|
+
});
|
|
5285
|
+
}
|
|
5224
5286
|
function validateSelectLike(query) {
|
|
5225
5287
|
if (query.type === "SELECT") validateSelect(query);
|
|
5226
5288
|
else if (query.type === "UNION") validateUnion(query);
|
|
@@ -5289,6 +5351,25 @@ function containsKlike(node) {
|
|
|
5289
5351
|
});
|
|
5290
5352
|
return found;
|
|
5291
5353
|
}
|
|
5354
|
+
function containsKlikeOutsideWhereAndNestedSelects(node, allowedWhere) {
|
|
5355
|
+
let found = false;
|
|
5356
|
+
const visit = (value) => {
|
|
5357
|
+
if (found || value === allowedWhere || value === null || typeof value !== "object") return;
|
|
5358
|
+
if (Array.isArray(value)) {
|
|
5359
|
+
for (const item of value) visit(item);
|
|
5360
|
+
return;
|
|
5361
|
+
}
|
|
5362
|
+
const obj = value;
|
|
5363
|
+
if (obj.type === "SELECT") return;
|
|
5364
|
+
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) {
|
|
5365
|
+
found = true;
|
|
5366
|
+
return;
|
|
5367
|
+
}
|
|
5368
|
+
for (const child of Object.values(obj)) visit(child);
|
|
5369
|
+
};
|
|
5370
|
+
visit(node);
|
|
5371
|
+
return found;
|
|
5372
|
+
}
|
|
5292
5373
|
function isDescendantOf(root, target) {
|
|
5293
5374
|
if (root === null) return false;
|
|
5294
5375
|
if (root === target) return true;
|
|
@@ -6598,11 +6679,6 @@ function evaluateCustomChecks(groups, row, resolveFieldType) {
|
|
|
6598
6679
|
|
|
6599
6680
|
// src/converter/dmlToKintone.ts
|
|
6600
6681
|
function assertDmlWhereIsSafe(where) {
|
|
6601
|
-
if (whereHasKlike(where)) {
|
|
6602
|
-
throw new DmlConvertError(
|
|
6603
|
-
"UPDATE / DELETE \u306E WHERE \u306B KLIKE / NOT KLIKE \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002kintone \u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22\u306E\u6253\u3061\u5207\u308A\u3092\u691C\u51FA\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u5168 DML \u3067\u5B89\u5168\u4E0A\u62D2\u5426\u3057\u3066\u3044\u307E\u3059\u3002"
|
|
6604
|
-
);
|
|
6605
|
-
}
|
|
6606
6682
|
if (!whereHasLike(where)) return;
|
|
6607
6683
|
throw new DmlConvertError(
|
|
6608
6684
|
"UPDATE / DELETE \u306E WHERE \u306B LIKE / NOT LIKE \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002LIKE \u306F kSQL \u306E\u610F\u5473\u8AD6\u306B\u5F93\u3063\u3066 JS \u3067\u8A55\u4FA1\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u304C\u3001\u89AA\u30EC\u30B3\u30FC\u30C9 DML \u306B\u306F JS \u8A55\u4FA1\u7D4C\u8DEF\u304C\u306A\u3044\u305F\u3081\u3001\u5B89\u5168\u4E0A\u62D2\u5426\u3057\u307E\u3057\u305F\u3002SELECT \u3067\u5BFE\u8C61\u30EC\u30B3\u30FC\u30C9\u756A\u53F7\u3092\u78BA\u8A8D\u3057\u3001IN \u307E\u305F\u306F\u5B8C\u5168\u4E00\u81F4\u3067\u5BFE\u8C61\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
@@ -9368,6 +9444,21 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
9368
9444
|
};
|
|
9369
9445
|
}
|
|
9370
9446
|
|
|
9447
|
+
// src/core/optimization/applyParentSelectionPlan.ts
|
|
9448
|
+
function buildApplyParentSelectionPlan(where, metadata) {
|
|
9449
|
+
const plan = buildSingleTableKlikePushdownPlan(where, {
|
|
9450
|
+
allowUnqualifiedFields: true,
|
|
9451
|
+
allowKlike: true,
|
|
9452
|
+
fieldTypes: metadata.fieldTypes,
|
|
9453
|
+
fieldOptions: metadata.fieldOptions
|
|
9454
|
+
});
|
|
9455
|
+
return {
|
|
9456
|
+
prefilter: plan.condition,
|
|
9457
|
+
appliedKlikes: plan.appliedKlikes,
|
|
9458
|
+
unappliedKlikes: plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr))
|
|
9459
|
+
};
|
|
9460
|
+
}
|
|
9461
|
+
|
|
9371
9462
|
// src/core/optimization/canonicalOrderPlanner.ts
|
|
9372
9463
|
var REST_OFFSET_MAX = 1e4;
|
|
9373
9464
|
var REST_LIMIT_MAX = 500;
|
|
@@ -11645,7 +11736,7 @@ function attachSearchAbortWarning(result, collector) {
|
|
|
11645
11736
|
}
|
|
11646
11737
|
async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
11647
11738
|
const unresolved = findVariableRef(stmt);
|
|
11648
|
-
if (unresolved !== null) {
|
|
11739
|
+
if (unresolved !== null && !isApplyParentKlikeStatement(stmt)) {
|
|
11649
11740
|
throw new Error(`ParseError: variable @${unresolved} is not defined in a batch.`);
|
|
11650
11741
|
}
|
|
11651
11742
|
assertApplyScope("phase15b", stmt);
|
|
@@ -16141,16 +16232,21 @@ async function executeMultipleParentApplyPreflight(stmt, client, options, cacheC
|
|
|
16141
16232
|
);
|
|
16142
16233
|
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
16143
16234
|
const metadata = resolveApplyPatchMetadata(stmt, fieldInfos);
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
|
|
16152
|
-
|
|
16153
|
-
|
|
16235
|
+
let snapshots;
|
|
16236
|
+
if (usesApplyParentResidualSelection(stmt)) {
|
|
16237
|
+
snapshots = await selectApplyParentSnapshots(stmt, client, options, fieldInfos, cacheContext);
|
|
16238
|
+
} else {
|
|
16239
|
+
const fields = collectApplySnapshotFields(stmt, fieldInfos);
|
|
16240
|
+
const baseQuery = updateToGetQuery(stmt).query;
|
|
16241
|
+
const detectionLimit = dmlMaxRows + 1;
|
|
16242
|
+
snapshots = await fetchAll(client.getRecords, stmt.appId, baseQuery, [...fields], {
|
|
16243
|
+
pageSize: Math.min(500, detectionLimit),
|
|
16244
|
+
parallel: options.fetchParallel ?? 1,
|
|
16245
|
+
maxRecords: detectionLimit,
|
|
16246
|
+
stopAfter: detectionLimit,
|
|
16247
|
+
onLimit: "error"
|
|
16248
|
+
});
|
|
16249
|
+
}
|
|
16154
16250
|
if (!stmt.validateOnly && snapshots.length > dmlMaxRows) {
|
|
16155
16251
|
throw new Error(`ArgumentError: APPLY parent rows (${snapshots.length}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
16156
16252
|
}
|
|
@@ -16182,6 +16278,95 @@ async function executeMultipleParentApplyPreflight(stmt, client, options, cacheC
|
|
|
16182
16278
|
const result = await executePreparedApplyWrite(prepared, client, diagnostic2);
|
|
16183
16279
|
return { ...result, diagnostic: withApplyDiagnosticProgress(diagnostic2, result) };
|
|
16184
16280
|
}
|
|
16281
|
+
function usesApplyParentResidualSelection(stmt) {
|
|
16282
|
+
return (stmt.applyBlocks?.length ?? 0) > 0 && !isSinglePositiveRecordIdWhere(stmt.where) && (whereHasLike(stmt.where) || whereHasKlike(stmt.where));
|
|
16283
|
+
}
|
|
16284
|
+
function isApplyParentKlikeStatement(stmt) {
|
|
16285
|
+
const target = stmt.type === "EXPLAIN" ? stmt.query : stmt;
|
|
16286
|
+
return target.type === "UPDATE" && usesApplyParentResidualSelection(target) && whereHasKlike(target.where);
|
|
16287
|
+
}
|
|
16288
|
+
var APPLY_PARENT_UNAPPLIED_KLIKE_ERROR = "APPLY \u8907\u6570\u89AA UPDATE \u306E\u89AA WHERE \u306B\u3001\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u306A\u3044 KLIKE / NOT KLIKE \u304C\u3042\u308A\u307E\u3059\u3002\nOR / NOT \u914D\u4E0B\u306A\u3069 native query \u3078\u5B8C\u5168\u306B\u9069\u7528\u3067\u304D\u306A\u3044 KLIKE \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\nWHERE \u3092 AND \u306E\u5B89\u5168\u306A KLIKE \u6761\u4EF6\u3078\u66F8\u304D\u63DB\u3048\u308B\u304B\u3001SELECT \u3067\u78BA\u8A8D\u3057\u305F $id IN (...) \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
|
|
16289
|
+
function assertApplyParentKlikesFullyApplied(plan) {
|
|
16290
|
+
if (plan.unappliedKlikes.length > 0) throw new Error(`UnsupportedError: ${APPLY_PARENT_UNAPPLIED_KLIKE_ERROR}`);
|
|
16291
|
+
}
|
|
16292
|
+
async function selectApplyParentSnapshots(stmt, client, options, fieldInfos, cacheContext) {
|
|
16293
|
+
const topLevelInfos = fieldInfos.filter((field) => !field.inSubtable);
|
|
16294
|
+
const infoByCode = new Map(topLevelInfos.map((field) => [field.code, field]));
|
|
16295
|
+
const fieldTypes = new Map(topLevelInfos.map((field) => [field.code, field.fieldType]));
|
|
16296
|
+
const fieldOptions = new Map(topLevelInfos.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
16297
|
+
const selectionPlan = buildApplyParentSelectionPlan(stmt.where, { fieldTypes, fieldOptions });
|
|
16298
|
+
assertApplyParentKlikesFullyApplied(selectionPlan);
|
|
16299
|
+
const prefilterQuery = selectionPlan.prefilter === null ? "" : whereToKintone(selectionPlan.prefilter);
|
|
16300
|
+
const fields = new Set(collectApplySnapshotFields(stmt, fieldInfos));
|
|
16301
|
+
for (const field of collectApplyParentWhereFields(stmt.where)) fields.add(field);
|
|
16302
|
+
const resolvers = await buildApplyParentFieldResolvers(
|
|
16303
|
+
stmt.appId,
|
|
16304
|
+
stmt.where,
|
|
16305
|
+
infoByCode,
|
|
16306
|
+
client,
|
|
16307
|
+
cacheContext
|
|
16308
|
+
);
|
|
16309
|
+
const candidates = await fetchAll(
|
|
16310
|
+
client.getRecords,
|
|
16311
|
+
stmt.appId,
|
|
16312
|
+
prefilterQuery,
|
|
16313
|
+
[...fields],
|
|
16314
|
+
{
|
|
16315
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
16316
|
+
parallel: options.fetchParallel ?? 1,
|
|
16317
|
+
onLimit: "error"
|
|
16318
|
+
// prefilter は target の超集合なので stopAfter を設定せず最後まで取得する。
|
|
16319
|
+
}
|
|
16320
|
+
);
|
|
16321
|
+
return candidates.map((snapshot) => ({ snapshot, row: flatten(snapshot, null) })).filter(({ row }) => evalWhere(
|
|
16322
|
+
stmt.where,
|
|
16323
|
+
row,
|
|
16324
|
+
resolvers.fieldTypeResolver,
|
|
16325
|
+
selectionPlan.appliedKlikes,
|
|
16326
|
+
resolvers.fieldSemanticsResolver
|
|
16327
|
+
)).map(({ snapshot }) => snapshot);
|
|
16328
|
+
}
|
|
16329
|
+
function collectApplyParentWhereFields(where) {
|
|
16330
|
+
return collectValidateWhereFields(where);
|
|
16331
|
+
}
|
|
16332
|
+
async function buildApplyParentFieldResolvers(appId, where, infoByCode, client, cacheContext) {
|
|
16333
|
+
const orderedFields = collectOrderedWhereFields(where);
|
|
16334
|
+
const needsStatusOrder = [...orderedFields].some((field) => infoByCode.get(field)?.fieldType === "STATUS");
|
|
16335
|
+
const statusOrder = needsStatusOrder ? await loadProcessStatusOrder(appId, client, cacheContext) : void 0;
|
|
16336
|
+
const fieldTypeResolver = (field) => field.field === "$id" ? "NUMBER" : infoByCode.get(field.field)?.fieldType;
|
|
16337
|
+
const fieldSemanticsResolver = (field) => {
|
|
16338
|
+
if (field.field === "$id") {
|
|
16339
|
+
return withFieldSemanticSource(resolveFieldSemantics({ fieldType: "__ID__" }), appId, "$id");
|
|
16340
|
+
}
|
|
16341
|
+
const info = infoByCode.get(field.field);
|
|
16342
|
+
if (!info) return void 0;
|
|
16343
|
+
const base = info.semantics ?? resolveFieldSemantics(info);
|
|
16344
|
+
const semantics = info.fieldType === "STATUS" && statusOrder ? { ...base, optionOrder: statusOrder } : base;
|
|
16345
|
+
return withFieldSemanticSource(semantics, appId, info.code);
|
|
16346
|
+
};
|
|
16347
|
+
return { fieldTypeResolver, fieldSemanticsResolver };
|
|
16348
|
+
}
|
|
16349
|
+
function collectOrderedWhereFields(where) {
|
|
16350
|
+
const fields = /* @__PURE__ */ new Set();
|
|
16351
|
+
const visit = (node) => {
|
|
16352
|
+
if (Array.isArray(node)) {
|
|
16353
|
+
node.forEach(visit);
|
|
16354
|
+
return;
|
|
16355
|
+
}
|
|
16356
|
+
if (node === null || typeof node !== "object") return;
|
|
16357
|
+
const value = node;
|
|
16358
|
+
if (value["type"] === "SELECT") return;
|
|
16359
|
+
if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
|
|
16360
|
+
const left = value["left"];
|
|
16361
|
+
if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
|
|
16362
|
+
fields.add(left["field"]);
|
|
16363
|
+
}
|
|
16364
|
+
}
|
|
16365
|
+
Object.values(value).forEach(visit);
|
|
16366
|
+
};
|
|
16367
|
+
visit(where);
|
|
16368
|
+
return fields;
|
|
16369
|
+
}
|
|
16185
16370
|
function materializePreparedApplyInsertValidation(stmt, prepared, fieldInfos) {
|
|
16186
16371
|
const errors = prepared.validations.flatMap((validation) => validation.errors);
|
|
16187
16372
|
const invalidRows = prepared.validations.reduce(
|
|
@@ -17067,6 +17252,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
17067
17252
|
return cache;
|
|
17068
17253
|
}
|
|
17069
17254
|
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
17255
|
+
var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
|
|
17070
17256
|
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4) {
|
|
17071
17257
|
const fieldApps = /* @__PURE__ */ new Set();
|
|
17072
17258
|
const processStatusApps = /* @__PURE__ */ new Set();
|
|
@@ -17191,12 +17377,19 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
17191
17377
|
const update = node;
|
|
17192
17378
|
const fields = await getFieldsCached(update.appId, tracedClient, cacheContext);
|
|
17193
17379
|
resolveApplyPatchMetadata(update, fields);
|
|
17380
|
+
if (usesApplyParentResidualSelection(update)) {
|
|
17381
|
+
const topLevel = fields.filter((field) => !field.inSubtable);
|
|
17382
|
+
const selectionPlan = buildApplyParentSelectionPlan(update.where, {
|
|
17383
|
+
fieldTypes: new Map(topLevel.map((field) => [field.code, field.fieldType])),
|
|
17384
|
+
fieldOptions: new Map(topLevel.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []))
|
|
17385
|
+
});
|
|
17386
|
+
applyParentExplainPlan.set(update, selectionPlan);
|
|
17387
|
+
}
|
|
17388
|
+
}
|
|
17389
|
+
const dml = node;
|
|
17390
|
+
if (dml.type !== "UPDATE" || !usesApplyParentResidualSelection(dml)) {
|
|
17391
|
+
await assertDmlWhereCapability(dml, tracedClient, cacheContext);
|
|
17194
17392
|
}
|
|
17195
|
-
await assertDmlWhereCapability(
|
|
17196
|
-
node,
|
|
17197
|
-
tracedClient,
|
|
17198
|
-
cacheContext
|
|
17199
|
-
);
|
|
17200
17393
|
}
|
|
17201
17394
|
await Promise.all(Object.values(typed).map(visit));
|
|
17202
17395
|
};
|
|
@@ -17367,7 +17560,8 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
|
|
|
17367
17560
|
analysis.capabilities,
|
|
17368
17561
|
analysis.orderPlans,
|
|
17369
17562
|
dmlMaxRows,
|
|
17370
|
-
dmlMaxSubtableRows
|
|
17563
|
+
dmlMaxSubtableRows,
|
|
17564
|
+
maxRecords
|
|
17371
17565
|
),
|
|
17372
17566
|
cursorMaxActive
|
|
17373
17567
|
)
|
|
@@ -17390,7 +17584,7 @@ function addCursorConcurrency(lines, cursorMaxActive) {
|
|
|
17390
17584
|
}
|
|
17391
17585
|
return result;
|
|
17392
17586
|
}
|
|
17393
|
-
function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
|
|
17587
|
+
function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
|
|
17394
17588
|
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
|
|
17395
17589
|
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
|
|
17396
17590
|
if (query.type === "INSERT") return buildInsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
@@ -17403,7 +17597,8 @@ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 1
|
|
|
17403
17597
|
capabilities,
|
|
17404
17598
|
orderPlans,
|
|
17405
17599
|
dmlMaxRows,
|
|
17406
|
-
dmlMaxSubtableRows
|
|
17600
|
+
dmlMaxSubtableRows,
|
|
17601
|
+
maxRecords
|
|
17407
17602
|
);
|
|
17408
17603
|
if (query.type === "DELETE") return buildDeletePlan(query, label);
|
|
17409
17604
|
if (query.type === "REORDER") return buildReorderPlan(query, label);
|
|
@@ -17709,9 +17904,9 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
17709
17904
|
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
17710
17905
|
return lines;
|
|
17711
17906
|
}
|
|
17712
|
-
function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
|
|
17907
|
+
function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
|
|
17713
17908
|
if (stmt.applyBlocks?.length) {
|
|
17714
|
-
return buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
17909
|
+
return buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows, maxRecords);
|
|
17715
17910
|
}
|
|
17716
17911
|
const isArith = hasArithAssignment(stmt);
|
|
17717
17912
|
const isStringFunc = stmt.assignments.some((a) => a.value.type === "STRING_FUNC");
|
|
@@ -17729,6 +17924,10 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100
|
|
|
17729
17924
|
} else {
|
|
17730
17925
|
lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
|
|
17731
17926
|
}
|
|
17927
|
+
if (!stmt.subtableCode) {
|
|
17928
|
+
lines.push(" selection: exact native pushdown; JS residual none");
|
|
17929
|
+
lines.push(" search abort: DML fail-closed (SearchAbortedError; mutation 0)");
|
|
17930
|
+
}
|
|
17732
17931
|
lines.push(isConstantFalseWhere(stmt.where) ? " api: metadata validation only (records API access: none)" : ` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
|
|
17733
17932
|
const setTypes = [];
|
|
17734
17933
|
if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
|
|
@@ -17754,7 +17953,7 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100
|
|
|
17754
17953
|
}
|
|
17755
17954
|
return lines;
|
|
17756
17955
|
}
|
|
17757
|
-
function buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows) {
|
|
17956
|
+
function buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows, maxRecords) {
|
|
17758
17957
|
const diagnostic2 = buildStaticApplyDiagnostic(stmt, dmlMaxRows, dmlMaxSubtableRows);
|
|
17759
17958
|
const branch = diagnostic2.branches[0];
|
|
17760
17959
|
const blocks = stmt.applyBlocks;
|
|
@@ -17768,12 +17967,24 @@ function buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows) {
|
|
|
17768
17967
|
});
|
|
17769
17968
|
const operationKinds = [...new Set(branch.targets.flatMap((target) => target.operations.map((operation) => operation.kind)))];
|
|
17770
17969
|
const hasRemove = operationKinds.includes("REMOVE");
|
|
17970
|
+
const selectionPlan = applyParentExplainPlan.get(stmt);
|
|
17971
|
+
const selectionLines = selectionPlan ? [
|
|
17972
|
+
"parent selection: safe prefilter + JS residual evaluation",
|
|
17973
|
+
`kintone prefilter: ${selectionPlan.prefilter === null ? "(none; empty query)" : whereToKintone(selectionPlan.prefilter)}`,
|
|
17974
|
+
"JS residual: original parent WHERE",
|
|
17975
|
+
`applied KLIKE: ${selectionPlan.appliedKlikes.size}`,
|
|
17976
|
+
`unapplied KLIKE: ${selectionPlan.unappliedKlikes.length}${selectionPlan.unappliedKlikes.length > 0 ? " (unsupported: cannot be fully applied to native query)" : ""}`,
|
|
17977
|
+
`candidate limit: maxRecords=${maxRecords}, onLimit=error, stopAfter=none`,
|
|
17978
|
+
`target guard: dmlMaxRows=${dmlMaxRows} after JS residual evaluation`,
|
|
17979
|
+
"search abort: DML fail-closed (B7-P3; all surfaces, no surface gate)"
|
|
17980
|
+
] : [];
|
|
17771
17981
|
return [
|
|
17772
17982
|
...label ? [label] : [],
|
|
17773
17983
|
"statement: UPDATE APPLY",
|
|
17774
17984
|
`target app: APP${stmt.appId}`,
|
|
17775
17985
|
`parent selector: ${safeWhereToKintone(stmt.where)}`,
|
|
17776
|
-
|
|
17986
|
+
`parent cardinality: ${isSinglePositiveRecordIdWhere(stmt.where) ? "single" : "multiple"}`,
|
|
17987
|
+
...selectionLines,
|
|
17777
17988
|
`apply target: ${branch.targets.map((target) => `${target.field} (${target.targetKind})`).join(" | ")}`,
|
|
17778
17989
|
`operations: ${operationKinds.join(" | ")}`,
|
|
17779
17990
|
`selector: ${selectorKinds.join(" | ")}`,
|
|
@@ -17799,6 +18010,10 @@ function buildDeletePlan(stmt, label) {
|
|
|
17799
18010
|
lines.push(` [DELETE]`);
|
|
17800
18011
|
lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
|
|
17801
18012
|
lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
|
|
18013
|
+
if (!stmt.subtableCode) {
|
|
18014
|
+
lines.push(" selection: exact native pushdown; JS residual none");
|
|
18015
|
+
lines.push(" search abort: DML fail-closed (SearchAbortedError; mutation 0)");
|
|
18016
|
+
}
|
|
17802
18017
|
lines.push(isConstantFalseWhere(stmt.where) ? " api: metadata validation only (records API access: none)" : ` api: GET /k/v1/records.json \u2192 DELETE /k/v1/records.json`);
|
|
17803
18018
|
return lines;
|
|
17804
18019
|
}
|
|
@@ -18802,6 +19017,12 @@ function getCursorLeaseManager(host, maxActive = DEFAULT_MAX_ACTIVE) {
|
|
|
18802
19017
|
return manager;
|
|
18803
19018
|
}
|
|
18804
19019
|
|
|
19020
|
+
// src/core/searchAbortWarning.ts
|
|
19021
|
+
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
19022
|
+
function isSearchAbortedWarning(value) {
|
|
19023
|
+
return value?.includes(SEARCH_ABORTED_HEADER_VALUE) === true;
|
|
19024
|
+
}
|
|
19025
|
+
|
|
18805
19026
|
// src/node/kintoneMetadata.ts
|
|
18806
19027
|
var KINTONE_METADATA_RESOURCES = [
|
|
18807
19028
|
"app",
|
|
@@ -18994,7 +19215,6 @@ var KintoneApiError = class extends Error {
|
|
|
18994
19215
|
this.name = "KintoneApiError";
|
|
18995
19216
|
}
|
|
18996
19217
|
};
|
|
18997
|
-
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
18998
19218
|
function createNodeKintoneConnection(baseUrl, tokenResolver) {
|
|
18999
19219
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
19000
19220
|
const apiBasePath = tokenResolver.guestSpaceId && tokenResolver.guestSpaceId > 0 ? `/k/guest/${tokenResolver.guestSpaceId}/v1` : "/k/v1";
|
|
@@ -19052,10 +19272,9 @@ function createNodeKintoneConnection(baseUrl, tokenResolver) {
|
|
|
19052
19272
|
if (tokenResolver.debug) {
|
|
19053
19273
|
tokenResolver.log?.(`[debug] response status=${res.status}`);
|
|
19054
19274
|
}
|
|
19055
|
-
const warning = res.headers.get("X-Cybozu-Warning") ?? "";
|
|
19056
19275
|
return {
|
|
19057
19276
|
response: res,
|
|
19058
|
-
searchAborted:
|
|
19277
|
+
searchAborted: isSearchAbortedWarning(res.headers.get("X-Cybozu-Warning"))
|
|
19059
19278
|
};
|
|
19060
19279
|
}
|
|
19061
19280
|
async function requestJsonResponse(path, init, appIdForToken) {
|