@rex0220/kintone-sql-tools 3.9.0 → 3.11.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 +405 -85
- package/dist-mcp/ksql-mcp.js +409 -87
- 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);
|
|
@@ -4930,6 +4930,9 @@ function canInlineSingleCte(stmt) {
|
|
|
4930
4930
|
if (stmt.ctes.length !== 1) return false;
|
|
4931
4931
|
const cteDef = stmt.ctes[0];
|
|
4932
4932
|
if (cteDef.query.type !== "SELECT" || resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
|
|
4933
|
+
if (!cteDef.query.columns.every(
|
|
4934
|
+
(column) => column.type === "WILDCARD" || column.type === "FIELD" && (column.alias === null || column.alias === column.field)
|
|
4935
|
+
)) return false;
|
|
4933
4936
|
const finalQuery = stmt.query;
|
|
4934
4937
|
if (finalQuery.type !== "SELECT") return false;
|
|
4935
4938
|
if (finalQuery.from.cteName !== cteDef.name || finalQuery.joins.length > 0) return false;
|
|
@@ -5082,31 +5085,31 @@ function isTargetField(field, options) {
|
|
|
5082
5085
|
}
|
|
5083
5086
|
|
|
5084
5087
|
// src/core/optimization/klikePushdownPlan.ts
|
|
5088
|
+
function buildSingleTableKlikePushdownPlan(where, options = {}) {
|
|
5089
|
+
const condition = where !== null && options.extractCondition !== false ? extractSafePushdownLeaves(where, options) : null;
|
|
5090
|
+
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
5091
|
+
collectKlikes(condition, appliedKlikes);
|
|
5092
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
5093
|
+
collectKlikes(where, allKlikes);
|
|
5094
|
+
return { condition, appliedKlikes, allKlikes: [...allKlikes] };
|
|
5095
|
+
}
|
|
5085
5096
|
function buildKlikePushdownPlan(stmt, options = {}) {
|
|
5086
5097
|
const joinsAreSafeForKlike = stmt.joins.every((join2) => join2.type === "INNER");
|
|
5087
5098
|
const common = {
|
|
5088
5099
|
allowKlike: joinsAreSafeForKlike,
|
|
5089
5100
|
allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
|
|
5090
5101
|
};
|
|
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
|
-
}
|
|
5102
|
+
const mainIsPhysical = !stmt.from.subtableCode && stmt.from.cteName === null;
|
|
5103
|
+
const mainHasUsableAlias = stmt.joins.length === 0 || stmt.from.alias !== null;
|
|
5104
|
+
const mainPlan = buildSingleTableKlikePushdownPlan(stmt.where, {
|
|
5105
|
+
...common,
|
|
5106
|
+
extractCondition: mainIsPhysical && mainHasUsableAlias,
|
|
5107
|
+
tableAlias: stmt.from.alias ?? void 0,
|
|
5108
|
+
allowUnqualifiedFields: stmt.joins.length === 0,
|
|
5109
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
5110
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
5111
|
+
});
|
|
5112
|
+
const mainCondition = mainPlan.condition;
|
|
5110
5113
|
const joinConditions = /* @__PURE__ */ new Map();
|
|
5111
5114
|
if (stmt.where !== null) {
|
|
5112
5115
|
for (const join2 of stmt.joins) {
|
|
@@ -5120,16 +5123,13 @@ function buildKlikePushdownPlan(stmt, options = {}) {
|
|
|
5120
5123
|
if (condition !== null) joinConditions.set(join2.table.alias, condition);
|
|
5121
5124
|
}
|
|
5122
5125
|
}
|
|
5123
|
-
const appliedKlikes =
|
|
5124
|
-
collectKlikes(mainCondition, appliedKlikes);
|
|
5126
|
+
const appliedKlikes = new Set(mainPlan.appliedKlikes);
|
|
5125
5127
|
for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
|
|
5126
|
-
const allKlikes = /* @__PURE__ */ new Set();
|
|
5127
|
-
collectKlikes(stmt.where, allKlikes);
|
|
5128
5128
|
return {
|
|
5129
5129
|
mainCondition,
|
|
5130
5130
|
joinConditions,
|
|
5131
5131
|
appliedKlikes,
|
|
5132
|
-
allKlikes:
|
|
5132
|
+
allKlikes: mainPlan.allKlikes
|
|
5133
5133
|
};
|
|
5134
5134
|
}
|
|
5135
5135
|
function unappliedKlikes(plan) {
|
|
@@ -5197,16 +5197,65 @@ function validateStatement(stmt) {
|
|
|
5197
5197
|
case "ASSERT":
|
|
5198
5198
|
validateNestedSelects(stmt);
|
|
5199
5199
|
return;
|
|
5200
|
+
case "UPDATE":
|
|
5201
|
+
if (stmt.applyBlocks?.length && !isSinglePositiveRecordIdWhere(stmt.where) && whereHasKlike(stmt.where)) {
|
|
5202
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
5203
|
+
validateNestedSelects(stmt);
|
|
5204
|
+
return;
|
|
5205
|
+
}
|
|
5206
|
+
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
5207
|
+
throw new KlikeValidationError(
|
|
5208
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5209
|
+
);
|
|
5210
|
+
}
|
|
5211
|
+
if (!stmt.applyBlocks?.length && !stmt.subtableCode && whereHasKlike(stmt.where) && !containsKlikeOutsideWhereAndNestedSelects(stmt, stmt.where)) {
|
|
5212
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
5213
|
+
validateNestedSelects(stmt);
|
|
5214
|
+
return;
|
|
5215
|
+
}
|
|
5216
|
+
if (containsKlike(stmt)) {
|
|
5217
|
+
throw new KlikeValidationError(
|
|
5218
|
+
"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"
|
|
5219
|
+
);
|
|
5220
|
+
}
|
|
5221
|
+
return;
|
|
5222
|
+
case "DELETE":
|
|
5223
|
+
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
5224
|
+
throw new KlikeValidationError(
|
|
5225
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB DELETE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5226
|
+
);
|
|
5227
|
+
}
|
|
5228
|
+
if (!stmt.subtableCode && whereHasKlike(stmt.where)) {
|
|
5229
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
5230
|
+
validateNestedSelects(stmt);
|
|
5231
|
+
return;
|
|
5232
|
+
}
|
|
5233
|
+
if (containsKlike(stmt)) {
|
|
5234
|
+
throw new KlikeValidationError(
|
|
5235
|
+
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA DELETE \u306E WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
5236
|
+
);
|
|
5237
|
+
}
|
|
5238
|
+
return;
|
|
5200
5239
|
case "INSERT":
|
|
5201
5240
|
case "INSERT_SELECT":
|
|
5241
|
+
if (containsKlike(stmt)) {
|
|
5242
|
+
throw new KlikeValidationError(
|
|
5243
|
+
"KLIKE / NOT KLIKE \u306F INSERT / INSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5244
|
+
);
|
|
5245
|
+
}
|
|
5246
|
+
return;
|
|
5202
5247
|
case "UPSERT":
|
|
5203
5248
|
case "UPSERT_SELECT":
|
|
5204
|
-
|
|
5205
|
-
|
|
5249
|
+
if (containsKlike(stmt)) {
|
|
5250
|
+
throw new KlikeValidationError(
|
|
5251
|
+
"KLIKE / NOT KLIKE \u306F UPSERT / UPSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5252
|
+
);
|
|
5253
|
+
}
|
|
5254
|
+
return;
|
|
5206
5255
|
case "REORDER":
|
|
5207
5256
|
if (containsKlike(stmt)) {
|
|
5208
5257
|
throw new KlikeValidationError(
|
|
5209
|
-
"KLIKE / NOT KLIKE \u306F\
|
|
5258
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB REORDER \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
5210
5259
|
);
|
|
5211
5260
|
}
|
|
5212
5261
|
return;
|
|
@@ -5221,6 +5270,22 @@ function validateStatement(stmt) {
|
|
|
5221
5270
|
return;
|
|
5222
5271
|
}
|
|
5223
5272
|
}
|
|
5273
|
+
function validateKlikeWhereExpressions(where) {
|
|
5274
|
+
walkWithoutNestedSelects(where, (expr) => {
|
|
5275
|
+
if (!isKlike(expr)) return;
|
|
5276
|
+
const right = expr.right;
|
|
5277
|
+
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
5278
|
+
throw new KlikeValidationError(
|
|
5279
|
+
"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"
|
|
5280
|
+
);
|
|
5281
|
+
}
|
|
5282
|
+
if (right.type === "STRING" && right.value.includes("%")) {
|
|
5283
|
+
throw new KlikeValidationError(
|
|
5284
|
+
"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"
|
|
5285
|
+
);
|
|
5286
|
+
}
|
|
5287
|
+
});
|
|
5288
|
+
}
|
|
5224
5289
|
function validateSelectLike(query) {
|
|
5225
5290
|
if (query.type === "SELECT") validateSelect(query);
|
|
5226
5291
|
else if (query.type === "UNION") validateUnion(query);
|
|
@@ -5289,6 +5354,25 @@ function containsKlike(node) {
|
|
|
5289
5354
|
});
|
|
5290
5355
|
return found;
|
|
5291
5356
|
}
|
|
5357
|
+
function containsKlikeOutsideWhereAndNestedSelects(node, allowedWhere) {
|
|
5358
|
+
let found = false;
|
|
5359
|
+
const visit = (value) => {
|
|
5360
|
+
if (found || value === allowedWhere || value === null || typeof value !== "object") return;
|
|
5361
|
+
if (Array.isArray(value)) {
|
|
5362
|
+
for (const item of value) visit(item);
|
|
5363
|
+
return;
|
|
5364
|
+
}
|
|
5365
|
+
const obj = value;
|
|
5366
|
+
if (obj.type === "SELECT") return;
|
|
5367
|
+
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) {
|
|
5368
|
+
found = true;
|
|
5369
|
+
return;
|
|
5370
|
+
}
|
|
5371
|
+
for (const child of Object.values(obj)) visit(child);
|
|
5372
|
+
};
|
|
5373
|
+
visit(node);
|
|
5374
|
+
return found;
|
|
5375
|
+
}
|
|
5292
5376
|
function isDescendantOf(root, target) {
|
|
5293
5377
|
if (root === null) return false;
|
|
5294
5378
|
if (root === target) return true;
|
|
@@ -6598,11 +6682,6 @@ function evaluateCustomChecks(groups, row, resolveFieldType) {
|
|
|
6598
6682
|
|
|
6599
6683
|
// src/converter/dmlToKintone.ts
|
|
6600
6684
|
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
6685
|
if (!whereHasLike(where)) return;
|
|
6607
6686
|
throw new DmlConvertError(
|
|
6608
6687
|
"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 +9447,21 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
9368
9447
|
};
|
|
9369
9448
|
}
|
|
9370
9449
|
|
|
9450
|
+
// src/core/optimization/applyParentSelectionPlan.ts
|
|
9451
|
+
function buildApplyParentSelectionPlan(where, metadata) {
|
|
9452
|
+
const plan = buildSingleTableKlikePushdownPlan(where, {
|
|
9453
|
+
allowUnqualifiedFields: true,
|
|
9454
|
+
allowKlike: true,
|
|
9455
|
+
fieldTypes: metadata.fieldTypes,
|
|
9456
|
+
fieldOptions: metadata.fieldOptions
|
|
9457
|
+
});
|
|
9458
|
+
return {
|
|
9459
|
+
prefilter: plan.condition,
|
|
9460
|
+
appliedKlikes: plan.appliedKlikes,
|
|
9461
|
+
unappliedKlikes: plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr))
|
|
9462
|
+
};
|
|
9463
|
+
}
|
|
9464
|
+
|
|
9371
9465
|
// src/core/optimization/canonicalOrderPlanner.ts
|
|
9372
9466
|
var REST_OFFSET_MAX = 1e4;
|
|
9373
9467
|
var REST_LIMIT_MAX = 500;
|
|
@@ -9609,10 +9703,12 @@ function flatten(record, alias) {
|
|
|
9609
9703
|
}
|
|
9610
9704
|
return row;
|
|
9611
9705
|
}
|
|
9612
|
-
function applyJoin(leftRows, rightRows, join2) {
|
|
9706
|
+
function applyJoin(leftRows, rightRows, join2, columns = {}) {
|
|
9613
9707
|
const { on, type: joinType } = join2;
|
|
9614
9708
|
const leftKey = on.left.tableAlias ? `${on.left.tableAlias}.${on.left.field}` : on.left.field;
|
|
9615
9709
|
const rightKey = on.right.tableAlias ? `${on.right.tableAlias}.${on.right.field}` : on.right.field;
|
|
9710
|
+
assertJoinKeyAvailable(leftRows, leftKey, columns.leftColumns);
|
|
9711
|
+
assertJoinKeyAvailable(rightRows, rightKey, columns.rightColumns);
|
|
9616
9712
|
if (joinType === "RIGHT") {
|
|
9617
9713
|
const leftIndex = /* @__PURE__ */ new Map();
|
|
9618
9714
|
for (const lRow of leftRows) {
|
|
@@ -9622,7 +9718,7 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
9622
9718
|
else leftIndex.set(k, [lRow]);
|
|
9623
9719
|
}
|
|
9624
9720
|
const emptyLeft = {};
|
|
9625
|
-
for (const key of Object.keys(leftRows[0] ?? {})) emptyLeft[key] = "";
|
|
9721
|
+
for (const key of columns.leftColumns ?? Object.keys(leftRows[0] ?? {})) emptyLeft[key] = "";
|
|
9626
9722
|
const result2 = [];
|
|
9627
9723
|
for (const rRow of rightRows) {
|
|
9628
9724
|
const k = rRow[rightKey] ?? "";
|
|
@@ -9644,7 +9740,7 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
9644
9740
|
}
|
|
9645
9741
|
const result = [];
|
|
9646
9742
|
const emptyRight = {};
|
|
9647
|
-
for (const key of Object.keys(rightRows[0] ?? {})) emptyRight[key] = "";
|
|
9743
|
+
for (const key of columns.rightColumns ?? Object.keys(rightRows[0] ?? {})) emptyRight[key] = "";
|
|
9648
9744
|
for (const lRow of leftRows) {
|
|
9649
9745
|
const k = lRow[leftKey] ?? "";
|
|
9650
9746
|
const matched = rightIndex.get(k) ?? [];
|
|
@@ -9658,6 +9754,12 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
9658
9754
|
}
|
|
9659
9755
|
return result;
|
|
9660
9756
|
}
|
|
9757
|
+
function assertJoinKeyAvailable(rows, key, savedColumns) {
|
|
9758
|
+
const missing = rows.length > 0 ? rows.some((row) => !Object.prototype.hasOwnProperty.call(row, key)) : savedColumns !== void 0 && !savedColumns.includes(key);
|
|
9759
|
+
if (missing) {
|
|
9760
|
+
throw new Error(`ArgumentError: JOIN key ${key} is not available in the materialized table.`);
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9661
9763
|
function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
9662
9764
|
if (where === null) return rows;
|
|
9663
9765
|
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
|
|
@@ -9956,9 +10058,12 @@ function applyLimit(rows, limit, offset) {
|
|
|
9956
10058
|
if (limit === null) return rows.slice(start);
|
|
9957
10059
|
return rows.slice(start, start + limit);
|
|
9958
10060
|
}
|
|
9959
|
-
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2) {
|
|
10061
|
+
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2, hiddenQualifiedAliases) {
|
|
9960
10062
|
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
9961
|
-
const projected2 = rows.map((row) =>
|
|
10063
|
+
const projected2 = rows.map((row) => stripHiddenQualifiedColumns(
|
|
10064
|
+
stripParentShortcutColumns(row),
|
|
10065
|
+
hiddenQualifiedAliases
|
|
10066
|
+
));
|
|
9962
10067
|
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
|
|
9963
10068
|
return { rows: projected2, columns: cols };
|
|
9964
10069
|
}
|
|
@@ -9978,7 +10083,10 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
|
|
|
9978
10083
|
case "VARIABLE_COL":
|
|
9979
10084
|
throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
|
|
9980
10085
|
case "WILDCARD":
|
|
9981
|
-
Object.assign(out,
|
|
10086
|
+
Object.assign(out, stripHiddenQualifiedColumns(
|
|
10087
|
+
stripParentShortcutColumns(row),
|
|
10088
|
+
hiddenQualifiedAliases
|
|
10089
|
+
));
|
|
9982
10090
|
break;
|
|
9983
10091
|
case "PARENT_WILDCARD": {
|
|
9984
10092
|
const parentKeys = Object.keys(row).filter((k) => k.startsWith("_p.")).sort();
|
|
@@ -10231,6 +10339,27 @@ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
|
10231
10339
|
args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
|
|
10232
10340
|
};
|
|
10233
10341
|
}
|
|
10342
|
+
function stripHiddenQualifiedColumns(row, hiddenQualifiedAliases) {
|
|
10343
|
+
if (!hiddenQualifiedAliases || hiddenQualifiedAliases.size === 0) return row;
|
|
10344
|
+
const out = {};
|
|
10345
|
+
for (const [key, value] of Object.entries(row)) {
|
|
10346
|
+
if ([...hiddenQualifiedAliases].some((alias) => key.startsWith(`${alias}.`))) continue;
|
|
10347
|
+
out[key] = value;
|
|
10348
|
+
}
|
|
10349
|
+
return out;
|
|
10350
|
+
}
|
|
10351
|
+
function flattenedColumns(columns, alias) {
|
|
10352
|
+
if (columns === void 0) return void 0;
|
|
10353
|
+
return alias ? columns.flatMap((column) => [`${alias}.${column}`, column]) : [...columns];
|
|
10354
|
+
}
|
|
10355
|
+
function mergeKnownColumns(left, right, rows) {
|
|
10356
|
+
if (left === void 0 && right === void 0 && rows.length === 0) return void 0;
|
|
10357
|
+
return [.../* @__PURE__ */ new Set([
|
|
10358
|
+
...left ?? [],
|
|
10359
|
+
...right ?? [],
|
|
10360
|
+
...Object.keys(rows[0] ?? {})
|
|
10361
|
+
])];
|
|
10362
|
+
}
|
|
10234
10363
|
function deriveOutputOrderSemantics(columns) {
|
|
10235
10364
|
const result = /* @__PURE__ */ new Map();
|
|
10236
10365
|
for (const column of columns) {
|
|
@@ -10265,7 +10394,9 @@ function runFullScan(input) {
|
|
|
10265
10394
|
havingFieldSemanticsResolver,
|
|
10266
10395
|
aggregateSortKindResolver,
|
|
10267
10396
|
appliedKlikes,
|
|
10268
|
-
sourceColumns
|
|
10397
|
+
sourceColumns,
|
|
10398
|
+
tableColumns,
|
|
10399
|
+
hiddenQualifiedAliases
|
|
10269
10400
|
} = input;
|
|
10270
10401
|
const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
|
|
10271
10402
|
for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
|
|
@@ -10273,11 +10404,25 @@ function runFullScan(input) {
|
|
|
10273
10404
|
const mainAlias = stmt.from.alias;
|
|
10274
10405
|
const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
|
|
10275
10406
|
rows = mainRecords.map((r) => flatten(r, mainAlias));
|
|
10407
|
+
let knownColumns = mergeKnownColumns(
|
|
10408
|
+
flattenedColumns(tableColumns?.get(mainAlias), mainAlias),
|
|
10409
|
+
void 0,
|
|
10410
|
+
rows
|
|
10411
|
+
);
|
|
10276
10412
|
for (const join2 of stmt.joins) {
|
|
10277
10413
|
const rightAlias = join2.table.alias;
|
|
10278
10414
|
const rightRecords = tables.get(rightAlias) ?? [];
|
|
10279
10415
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
10280
|
-
|
|
10416
|
+
const rightColumns = mergeKnownColumns(
|
|
10417
|
+
flattenedColumns(tableColumns?.get(rightAlias), rightAlias),
|
|
10418
|
+
void 0,
|
|
10419
|
+
rightRows
|
|
10420
|
+
);
|
|
10421
|
+
rows = applyJoin(rows, rightRows, join2, {
|
|
10422
|
+
leftColumns: knownColumns,
|
|
10423
|
+
rightColumns
|
|
10424
|
+
});
|
|
10425
|
+
knownColumns = mergeKnownColumns(knownColumns, rightColumns, rows);
|
|
10281
10426
|
}
|
|
10282
10427
|
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
|
|
10283
10428
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
@@ -10290,7 +10435,15 @@ function runFullScan(input) {
|
|
|
10290
10435
|
}
|
|
10291
10436
|
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
10292
10437
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
10293
|
-
return project(
|
|
10438
|
+
return project(
|
|
10439
|
+
rows,
|
|
10440
|
+
stmt.columns,
|
|
10441
|
+
scalarCache,
|
|
10442
|
+
fieldTypeResolver,
|
|
10443
|
+
sourceColumns,
|
|
10444
|
+
fieldSemanticsResolver,
|
|
10445
|
+
hiddenQualifiedAliases
|
|
10446
|
+
);
|
|
10294
10447
|
}
|
|
10295
10448
|
|
|
10296
10449
|
// src/converter/subtableAdapter.ts
|
|
@@ -11645,7 +11798,7 @@ function attachSearchAbortWarning(result, collector) {
|
|
|
11645
11798
|
}
|
|
11646
11799
|
async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
11647
11800
|
const unresolved = findVariableRef(stmt);
|
|
11648
|
-
if (unresolved !== null) {
|
|
11801
|
+
if (unresolved !== null && !isApplyParentKlikeStatement(stmt)) {
|
|
11649
11802
|
throw new Error(`ParseError: variable @${unresolved} is not defined in a batch.`);
|
|
11650
11803
|
}
|
|
11651
11804
|
assertApplyScope("phase15b", stmt);
|
|
@@ -12602,7 +12755,7 @@ async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, mate
|
|
|
12602
12755
|
if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
12603
12756
|
return fromPhysical(stmt.from, field.field, false);
|
|
12604
12757
|
}
|
|
12605
|
-
const table = tables.find((candidate) => candidate
|
|
12758
|
+
const table = tables.find((candidate) => effectiveTableAlias(candidate) === field.tableAlias);
|
|
12606
12759
|
if (!table) return void 0;
|
|
12607
12760
|
if (table.cteName !== null) {
|
|
12608
12761
|
return materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
|
|
@@ -13036,8 +13189,11 @@ function collectSelectTypedInFieldRefs(stmt) {
|
|
|
13036
13189
|
}
|
|
13037
13190
|
return refs;
|
|
13038
13191
|
}
|
|
13192
|
+
function effectiveTableAlias(table) {
|
|
13193
|
+
return table.alias ?? table.cteName;
|
|
13194
|
+
}
|
|
13039
13195
|
function findTableForAlias(stmt, alias) {
|
|
13040
|
-
return [stmt.from, ...stmt.joins.map((join2) => join2.table)].find((table) => table
|
|
13196
|
+
return [stmt.from, ...stmt.joins.map((join2) => join2.table)].find((table) => effectiveTableAlias(table) === alias);
|
|
13041
13197
|
}
|
|
13042
13198
|
function physicalSelectTables(stmt) {
|
|
13043
13199
|
return [stmt.from, ...stmt.joins.map((join2) => join2.table)].filter((table) => table.cteName === null);
|
|
@@ -13189,7 +13345,7 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
13189
13345
|
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
13190
13346
|
info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
|
|
13191
13347
|
} else {
|
|
13192
|
-
const table = tables.find((candidate) => candidate
|
|
13348
|
+
const table = tables.find((candidate) => effectiveTableAlias(candidate) === ref.tableAlias);
|
|
13193
13349
|
if (!table) return void 0;
|
|
13194
13350
|
if (table.cteName !== null) {
|
|
13195
13351
|
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
|
|
@@ -13214,7 +13370,7 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
13214
13370
|
return matches[0];
|
|
13215
13371
|
}
|
|
13216
13372
|
if (!info) return void 0;
|
|
13217
|
-
const sourceTable = ref.tableAlias !== null ? tables.find((table) => table
|
|
13373
|
+
const sourceTable = ref.tableAlias !== null ? tables.find((table) => effectiveTableAlias(table) === ref.tableAlias) : stmt.joins.length === 0 ? stmt.from : void 0;
|
|
13218
13374
|
return semanticsForInfo(info, sourceTable?.appId ?? stmt.from.appId);
|
|
13219
13375
|
};
|
|
13220
13376
|
}
|
|
@@ -13344,7 +13500,7 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
13344
13500
|
const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
|
|
13345
13501
|
return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
|
|
13346
13502
|
}
|
|
13347
|
-
const table = tables.find((candidate) => candidate
|
|
13503
|
+
const table = tables.find((candidate) => effectiveTableAlias(candidate) === ref.tableAlias);
|
|
13348
13504
|
if (!table) return void 0;
|
|
13349
13505
|
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
13350
13506
|
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
@@ -13449,7 +13605,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
13449
13605
|
if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
13450
13606
|
return fieldTypesByApp.get(stmt.from.appId)?.get(field.field);
|
|
13451
13607
|
}
|
|
13452
|
-
const table = tables.find((candidate) => candidate
|
|
13608
|
+
const table = tables.find((candidate) => effectiveTableAlias(candidate) === field.tableAlias);
|
|
13453
13609
|
if (!table || table.cteName !== null) return void 0;
|
|
13454
13610
|
return subtableSystemFieldType(table, field.field) ?? fieldTypesByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field.field));
|
|
13455
13611
|
}
|
|
@@ -13674,6 +13830,37 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
|
|
|
13674
13830
|
return result;
|
|
13675
13831
|
}
|
|
13676
13832
|
async function executeFullScanWithCte(stmt, client, options, cteCache, cacheContext) {
|
|
13833
|
+
const hiddenQualifiedAliases = /* @__PURE__ */ new Set();
|
|
13834
|
+
const withEffectiveAlias = (table) => {
|
|
13835
|
+
if (table.alias !== null || table.cteName === null) return table;
|
|
13836
|
+
const alias = effectiveTableAlias(table);
|
|
13837
|
+
hiddenQualifiedAliases.add(alias);
|
|
13838
|
+
return { ...table, alias };
|
|
13839
|
+
};
|
|
13840
|
+
stmt = {
|
|
13841
|
+
...stmt,
|
|
13842
|
+
from: withEffectiveAlias(stmt.from),
|
|
13843
|
+
joins: stmt.joins.map((join2) => ({ ...join2, table: withEffectiveAlias(join2.table) }))
|
|
13844
|
+
};
|
|
13845
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
13846
|
+
for (const table of [stmt.from, ...stmt.joins.map((join2) => join2.table)]) {
|
|
13847
|
+
const alias = effectiveTableAlias(table);
|
|
13848
|
+
if (alias === null) continue;
|
|
13849
|
+
if (aliases.has(alias)) {
|
|
13850
|
+
throw new Error(`ArgumentError: effective alias ${alias} is used by multiple tables.`);
|
|
13851
|
+
}
|
|
13852
|
+
aliases.add(alias);
|
|
13853
|
+
}
|
|
13854
|
+
const requireMaterializedTable = (name) => {
|
|
13855
|
+
const table = cteCache.get(name);
|
|
13856
|
+
if (!table) {
|
|
13857
|
+
throw new Error(`ArgumentError: materialized source ${name} is not available.`);
|
|
13858
|
+
}
|
|
13859
|
+
return table;
|
|
13860
|
+
};
|
|
13861
|
+
for (const table of [stmt.from, ...stmt.joins.map((join2) => join2.table)]) {
|
|
13862
|
+
if (table.cteName !== null) requireMaterializedTable(table.cteName);
|
|
13863
|
+
}
|
|
13677
13864
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
13678
13865
|
const warnings = /* @__PURE__ */ new Set();
|
|
13679
13866
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -13720,9 +13907,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
13720
13907
|
orderByMetaPromise.catch(() => {
|
|
13721
13908
|
});
|
|
13722
13909
|
const tables = /* @__PURE__ */ new Map();
|
|
13910
|
+
const tableColumns = /* @__PURE__ */ new Map();
|
|
13723
13911
|
if (stmt.from.cteName != null) {
|
|
13724
|
-
const table =
|
|
13725
|
-
tables.set(stmt.from.alias,
|
|
13912
|
+
const table = requireMaterializedTable(stmt.from.cteName);
|
|
13913
|
+
tables.set(stmt.from.alias, table.rows.map(processRowToKintoneRecord));
|
|
13914
|
+
tableColumns.set(stmt.from.alias, table.columns);
|
|
13726
13915
|
} else {
|
|
13727
13916
|
const mainRecords = await fetchTableRecordsForFullScan(
|
|
13728
13917
|
stmt,
|
|
@@ -13740,8 +13929,9 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
13740
13929
|
}
|
|
13741
13930
|
const joinFetches = stmt.joins.map(async (join2) => {
|
|
13742
13931
|
if (join2.table.cteName != null) {
|
|
13743
|
-
const table =
|
|
13744
|
-
tables.set(join2.table.alias,
|
|
13932
|
+
const table = requireMaterializedTable(join2.table.cteName);
|
|
13933
|
+
tables.set(join2.table.alias, table.rows.map(processRowToKintoneRecord));
|
|
13934
|
+
tableColumns.set(join2.table.alias, table.columns);
|
|
13745
13935
|
} else {
|
|
13746
13936
|
const pushDownCond = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
13747
13937
|
const optimized = await tryFetchJoinRecordsBySourceKeys(
|
|
@@ -13772,7 +13962,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
13772
13962
|
await Promise.all(joinFetches);
|
|
13773
13963
|
const scalarCache = await scalarCachePromise;
|
|
13774
13964
|
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
13775
|
-
const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ?
|
|
13965
|
+
const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? requireMaterializedTable(stmt.from.cteName).columns : void 0;
|
|
13776
13966
|
const { rows, columns } = runFullScan({
|
|
13777
13967
|
tables,
|
|
13778
13968
|
stmt,
|
|
@@ -13786,7 +13976,9 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
13786
13976
|
havingFieldSemanticsResolver,
|
|
13787
13977
|
aggregateSortKindResolver,
|
|
13788
13978
|
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
13789
|
-
sourceColumns
|
|
13979
|
+
sourceColumns,
|
|
13980
|
+
tableColumns,
|
|
13981
|
+
hiddenQualifiedAliases
|
|
13790
13982
|
});
|
|
13791
13983
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
13792
13984
|
}
|
|
@@ -14088,7 +14280,7 @@ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, material
|
|
|
14088
14280
|
const info2 = infosByApp.get(stmt.from.appId)?.get(ref.field);
|
|
14089
14281
|
return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
|
|
14090
14282
|
}
|
|
14091
|
-
const table = tables.find((candidate) => candidate
|
|
14283
|
+
const table = tables.find((candidate) => effectiveTableAlias(candidate) === ref.tableAlias);
|
|
14092
14284
|
if (!table) return void 0;
|
|
14093
14285
|
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
14094
14286
|
const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
@@ -16141,16 +16333,21 @@ async function executeMultipleParentApplyPreflight(stmt, client, options, cacheC
|
|
|
16141
16333
|
);
|
|
16142
16334
|
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
16143
16335
|
const metadata = resolveApplyPatchMetadata(stmt, fieldInfos);
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
|
|
16152
|
-
|
|
16153
|
-
|
|
16336
|
+
let snapshots;
|
|
16337
|
+
if (usesApplyParentResidualSelection(stmt)) {
|
|
16338
|
+
snapshots = await selectApplyParentSnapshots(stmt, client, options, fieldInfos, cacheContext);
|
|
16339
|
+
} else {
|
|
16340
|
+
const fields = collectApplySnapshotFields(stmt, fieldInfos);
|
|
16341
|
+
const baseQuery = updateToGetQuery(stmt).query;
|
|
16342
|
+
const detectionLimit = dmlMaxRows + 1;
|
|
16343
|
+
snapshots = await fetchAll(client.getRecords, stmt.appId, baseQuery, [...fields], {
|
|
16344
|
+
pageSize: Math.min(500, detectionLimit),
|
|
16345
|
+
parallel: options.fetchParallel ?? 1,
|
|
16346
|
+
maxRecords: detectionLimit,
|
|
16347
|
+
stopAfter: detectionLimit,
|
|
16348
|
+
onLimit: "error"
|
|
16349
|
+
});
|
|
16350
|
+
}
|
|
16154
16351
|
if (!stmt.validateOnly && snapshots.length > dmlMaxRows) {
|
|
16155
16352
|
throw new Error(`ArgumentError: APPLY parent rows (${snapshots.length}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
16156
16353
|
}
|
|
@@ -16182,6 +16379,95 @@ async function executeMultipleParentApplyPreflight(stmt, client, options, cacheC
|
|
|
16182
16379
|
const result = await executePreparedApplyWrite(prepared, client, diagnostic2);
|
|
16183
16380
|
return { ...result, diagnostic: withApplyDiagnosticProgress(diagnostic2, result) };
|
|
16184
16381
|
}
|
|
16382
|
+
function usesApplyParentResidualSelection(stmt) {
|
|
16383
|
+
return (stmt.applyBlocks?.length ?? 0) > 0 && !isSinglePositiveRecordIdWhere(stmt.where) && (whereHasLike(stmt.where) || whereHasKlike(stmt.where));
|
|
16384
|
+
}
|
|
16385
|
+
function isApplyParentKlikeStatement(stmt) {
|
|
16386
|
+
const target = stmt.type === "EXPLAIN" ? stmt.query : stmt;
|
|
16387
|
+
return target.type === "UPDATE" && usesApplyParentResidualSelection(target) && whereHasKlike(target.where);
|
|
16388
|
+
}
|
|
16389
|
+
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";
|
|
16390
|
+
function assertApplyParentKlikesFullyApplied(plan) {
|
|
16391
|
+
if (plan.unappliedKlikes.length > 0) throw new Error(`UnsupportedError: ${APPLY_PARENT_UNAPPLIED_KLIKE_ERROR}`);
|
|
16392
|
+
}
|
|
16393
|
+
async function selectApplyParentSnapshots(stmt, client, options, fieldInfos, cacheContext) {
|
|
16394
|
+
const topLevelInfos = fieldInfos.filter((field) => !field.inSubtable);
|
|
16395
|
+
const infoByCode = new Map(topLevelInfos.map((field) => [field.code, field]));
|
|
16396
|
+
const fieldTypes = new Map(topLevelInfos.map((field) => [field.code, field.fieldType]));
|
|
16397
|
+
const fieldOptions = new Map(topLevelInfos.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
16398
|
+
const selectionPlan = buildApplyParentSelectionPlan(stmt.where, { fieldTypes, fieldOptions });
|
|
16399
|
+
assertApplyParentKlikesFullyApplied(selectionPlan);
|
|
16400
|
+
const prefilterQuery = selectionPlan.prefilter === null ? "" : whereToKintone(selectionPlan.prefilter);
|
|
16401
|
+
const fields = new Set(collectApplySnapshotFields(stmt, fieldInfos));
|
|
16402
|
+
for (const field of collectApplyParentWhereFields(stmt.where)) fields.add(field);
|
|
16403
|
+
const resolvers = await buildApplyParentFieldResolvers(
|
|
16404
|
+
stmt.appId,
|
|
16405
|
+
stmt.where,
|
|
16406
|
+
infoByCode,
|
|
16407
|
+
client,
|
|
16408
|
+
cacheContext
|
|
16409
|
+
);
|
|
16410
|
+
const candidates = await fetchAll(
|
|
16411
|
+
client.getRecords,
|
|
16412
|
+
stmt.appId,
|
|
16413
|
+
prefilterQuery,
|
|
16414
|
+
[...fields],
|
|
16415
|
+
{
|
|
16416
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
16417
|
+
parallel: options.fetchParallel ?? 1,
|
|
16418
|
+
onLimit: "error"
|
|
16419
|
+
// prefilter は target の超集合なので stopAfter を設定せず最後まで取得する。
|
|
16420
|
+
}
|
|
16421
|
+
);
|
|
16422
|
+
return candidates.map((snapshot) => ({ snapshot, row: flatten(snapshot, null) })).filter(({ row }) => evalWhere(
|
|
16423
|
+
stmt.where,
|
|
16424
|
+
row,
|
|
16425
|
+
resolvers.fieldTypeResolver,
|
|
16426
|
+
selectionPlan.appliedKlikes,
|
|
16427
|
+
resolvers.fieldSemanticsResolver
|
|
16428
|
+
)).map(({ snapshot }) => snapshot);
|
|
16429
|
+
}
|
|
16430
|
+
function collectApplyParentWhereFields(where) {
|
|
16431
|
+
return collectValidateWhereFields(where);
|
|
16432
|
+
}
|
|
16433
|
+
async function buildApplyParentFieldResolvers(appId, where, infoByCode, client, cacheContext) {
|
|
16434
|
+
const orderedFields = collectOrderedWhereFields(where);
|
|
16435
|
+
const needsStatusOrder = [...orderedFields].some((field) => infoByCode.get(field)?.fieldType === "STATUS");
|
|
16436
|
+
const statusOrder = needsStatusOrder ? await loadProcessStatusOrder(appId, client, cacheContext) : void 0;
|
|
16437
|
+
const fieldTypeResolver = (field) => field.field === "$id" ? "NUMBER" : infoByCode.get(field.field)?.fieldType;
|
|
16438
|
+
const fieldSemanticsResolver = (field) => {
|
|
16439
|
+
if (field.field === "$id") {
|
|
16440
|
+
return withFieldSemanticSource(resolveFieldSemantics({ fieldType: "__ID__" }), appId, "$id");
|
|
16441
|
+
}
|
|
16442
|
+
const info = infoByCode.get(field.field);
|
|
16443
|
+
if (!info) return void 0;
|
|
16444
|
+
const base = info.semantics ?? resolveFieldSemantics(info);
|
|
16445
|
+
const semantics = info.fieldType === "STATUS" && statusOrder ? { ...base, optionOrder: statusOrder } : base;
|
|
16446
|
+
return withFieldSemanticSource(semantics, appId, info.code);
|
|
16447
|
+
};
|
|
16448
|
+
return { fieldTypeResolver, fieldSemanticsResolver };
|
|
16449
|
+
}
|
|
16450
|
+
function collectOrderedWhereFields(where) {
|
|
16451
|
+
const fields = /* @__PURE__ */ new Set();
|
|
16452
|
+
const visit = (node) => {
|
|
16453
|
+
if (Array.isArray(node)) {
|
|
16454
|
+
node.forEach(visit);
|
|
16455
|
+
return;
|
|
16456
|
+
}
|
|
16457
|
+
if (node === null || typeof node !== "object") return;
|
|
16458
|
+
const value = node;
|
|
16459
|
+
if (value["type"] === "SELECT") return;
|
|
16460
|
+
if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
|
|
16461
|
+
const left = value["left"];
|
|
16462
|
+
if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
|
|
16463
|
+
fields.add(left["field"]);
|
|
16464
|
+
}
|
|
16465
|
+
}
|
|
16466
|
+
Object.values(value).forEach(visit);
|
|
16467
|
+
};
|
|
16468
|
+
visit(where);
|
|
16469
|
+
return fields;
|
|
16470
|
+
}
|
|
16185
16471
|
function materializePreparedApplyInsertValidation(stmt, prepared, fieldInfos) {
|
|
16186
16472
|
const errors = prepared.validations.flatMap((validation) => validation.errors);
|
|
16187
16473
|
const invalidRows = prepared.validations.reduce(
|
|
@@ -17067,6 +17353,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
17067
17353
|
return cache;
|
|
17068
17354
|
}
|
|
17069
17355
|
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
17356
|
+
var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
|
|
17070
17357
|
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4) {
|
|
17071
17358
|
const fieldApps = /* @__PURE__ */ new Set();
|
|
17072
17359
|
const processStatusApps = /* @__PURE__ */ new Set();
|
|
@@ -17191,12 +17478,19 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
17191
17478
|
const update = node;
|
|
17192
17479
|
const fields = await getFieldsCached(update.appId, tracedClient, cacheContext);
|
|
17193
17480
|
resolveApplyPatchMetadata(update, fields);
|
|
17481
|
+
if (usesApplyParentResidualSelection(update)) {
|
|
17482
|
+
const topLevel = fields.filter((field) => !field.inSubtable);
|
|
17483
|
+
const selectionPlan = buildApplyParentSelectionPlan(update.where, {
|
|
17484
|
+
fieldTypes: new Map(topLevel.map((field) => [field.code, field.fieldType])),
|
|
17485
|
+
fieldOptions: new Map(topLevel.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []))
|
|
17486
|
+
});
|
|
17487
|
+
applyParentExplainPlan.set(update, selectionPlan);
|
|
17488
|
+
}
|
|
17489
|
+
}
|
|
17490
|
+
const dml = node;
|
|
17491
|
+
if (dml.type !== "UPDATE" || !usesApplyParentResidualSelection(dml)) {
|
|
17492
|
+
await assertDmlWhereCapability(dml, tracedClient, cacheContext);
|
|
17194
17493
|
}
|
|
17195
|
-
await assertDmlWhereCapability(
|
|
17196
|
-
node,
|
|
17197
|
-
tracedClient,
|
|
17198
|
-
cacheContext
|
|
17199
|
-
);
|
|
17200
17494
|
}
|
|
17201
17495
|
await Promise.all(Object.values(typed).map(visit));
|
|
17202
17496
|
};
|
|
@@ -17367,7 +17661,8 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
|
|
|
17367
17661
|
analysis.capabilities,
|
|
17368
17662
|
analysis.orderPlans,
|
|
17369
17663
|
dmlMaxRows,
|
|
17370
|
-
dmlMaxSubtableRows
|
|
17664
|
+
dmlMaxSubtableRows,
|
|
17665
|
+
maxRecords
|
|
17371
17666
|
),
|
|
17372
17667
|
cursorMaxActive
|
|
17373
17668
|
)
|
|
@@ -17390,7 +17685,7 @@ function addCursorConcurrency(lines, cursorMaxActive) {
|
|
|
17390
17685
|
}
|
|
17391
17686
|
return result;
|
|
17392
17687
|
}
|
|
17393
|
-
function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
|
|
17688
|
+
function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
|
|
17394
17689
|
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
|
|
17395
17690
|
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
|
|
17396
17691
|
if (query.type === "INSERT") return buildInsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
@@ -17403,7 +17698,8 @@ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 1
|
|
|
17403
17698
|
capabilities,
|
|
17404
17699
|
orderPlans,
|
|
17405
17700
|
dmlMaxRows,
|
|
17406
|
-
dmlMaxSubtableRows
|
|
17701
|
+
dmlMaxSubtableRows,
|
|
17702
|
+
maxRecords
|
|
17407
17703
|
);
|
|
17408
17704
|
if (query.type === "DELETE") return buildDeletePlan(query, label);
|
|
17409
17705
|
if (query.type === "REORDER") return buildReorderPlan(query, label);
|
|
@@ -17709,9 +18005,9 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
17709
18005
|
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
17710
18006
|
return lines;
|
|
17711
18007
|
}
|
|
17712
|
-
function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
|
|
18008
|
+
function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
|
|
17713
18009
|
if (stmt.applyBlocks?.length) {
|
|
17714
|
-
return buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
18010
|
+
return buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows, maxRecords);
|
|
17715
18011
|
}
|
|
17716
18012
|
const isArith = hasArithAssignment(stmt);
|
|
17717
18013
|
const isStringFunc = stmt.assignments.some((a) => a.value.type === "STRING_FUNC");
|
|
@@ -17729,6 +18025,10 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100
|
|
|
17729
18025
|
} else {
|
|
17730
18026
|
lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
|
|
17731
18027
|
}
|
|
18028
|
+
if (!stmt.subtableCode) {
|
|
18029
|
+
lines.push(" selection: exact native pushdown; JS residual none");
|
|
18030
|
+
lines.push(" search abort: DML fail-closed (SearchAbortedError; mutation 0)");
|
|
18031
|
+
}
|
|
17732
18032
|
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
18033
|
const setTypes = [];
|
|
17734
18034
|
if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
|
|
@@ -17754,7 +18054,7 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100
|
|
|
17754
18054
|
}
|
|
17755
18055
|
return lines;
|
|
17756
18056
|
}
|
|
17757
|
-
function buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows) {
|
|
18057
|
+
function buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows, maxRecords) {
|
|
17758
18058
|
const diagnostic2 = buildStaticApplyDiagnostic(stmt, dmlMaxRows, dmlMaxSubtableRows);
|
|
17759
18059
|
const branch = diagnostic2.branches[0];
|
|
17760
18060
|
const blocks = stmt.applyBlocks;
|
|
@@ -17768,12 +18068,24 @@ function buildUpdateApplyPlan(stmt, label, dmlMaxRows, dmlMaxSubtableRows) {
|
|
|
17768
18068
|
});
|
|
17769
18069
|
const operationKinds = [...new Set(branch.targets.flatMap((target) => target.operations.map((operation) => operation.kind)))];
|
|
17770
18070
|
const hasRemove = operationKinds.includes("REMOVE");
|
|
18071
|
+
const selectionPlan = applyParentExplainPlan.get(stmt);
|
|
18072
|
+
const selectionLines = selectionPlan ? [
|
|
18073
|
+
"parent selection: safe prefilter + JS residual evaluation",
|
|
18074
|
+
`kintone prefilter: ${selectionPlan.prefilter === null ? "(none; empty query)" : whereToKintone(selectionPlan.prefilter)}`,
|
|
18075
|
+
"JS residual: original parent WHERE",
|
|
18076
|
+
`applied KLIKE: ${selectionPlan.appliedKlikes.size}`,
|
|
18077
|
+
`unapplied KLIKE: ${selectionPlan.unappliedKlikes.length}${selectionPlan.unappliedKlikes.length > 0 ? " (unsupported: cannot be fully applied to native query)" : ""}`,
|
|
18078
|
+
`candidate limit: maxRecords=${maxRecords}, onLimit=error, stopAfter=none`,
|
|
18079
|
+
`target guard: dmlMaxRows=${dmlMaxRows} after JS residual evaluation`,
|
|
18080
|
+
"search abort: DML fail-closed (B7-P3; all surfaces, no surface gate)"
|
|
18081
|
+
] : [];
|
|
17771
18082
|
return [
|
|
17772
18083
|
...label ? [label] : [],
|
|
17773
18084
|
"statement: UPDATE APPLY",
|
|
17774
18085
|
`target app: APP${stmt.appId}`,
|
|
17775
18086
|
`parent selector: ${safeWhereToKintone(stmt.where)}`,
|
|
17776
|
-
|
|
18087
|
+
`parent cardinality: ${isSinglePositiveRecordIdWhere(stmt.where) ? "single" : "multiple"}`,
|
|
18088
|
+
...selectionLines,
|
|
17777
18089
|
`apply target: ${branch.targets.map((target) => `${target.field} (${target.targetKind})`).join(" | ")}`,
|
|
17778
18090
|
`operations: ${operationKinds.join(" | ")}`,
|
|
17779
18091
|
`selector: ${selectorKinds.join(" | ")}`,
|
|
@@ -17799,6 +18111,10 @@ function buildDeletePlan(stmt, label) {
|
|
|
17799
18111
|
lines.push(` [DELETE]`);
|
|
17800
18112
|
lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
|
|
17801
18113
|
lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
|
|
18114
|
+
if (!stmt.subtableCode) {
|
|
18115
|
+
lines.push(" selection: exact native pushdown; JS residual none");
|
|
18116
|
+
lines.push(" search abort: DML fail-closed (SearchAbortedError; mutation 0)");
|
|
18117
|
+
}
|
|
17802
18118
|
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
18119
|
return lines;
|
|
17804
18120
|
}
|
|
@@ -18802,6 +19118,12 @@ function getCursorLeaseManager(host, maxActive = DEFAULT_MAX_ACTIVE) {
|
|
|
18802
19118
|
return manager;
|
|
18803
19119
|
}
|
|
18804
19120
|
|
|
19121
|
+
// src/core/searchAbortWarning.ts
|
|
19122
|
+
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
19123
|
+
function isSearchAbortedWarning(value) {
|
|
19124
|
+
return value?.includes(SEARCH_ABORTED_HEADER_VALUE) === true;
|
|
19125
|
+
}
|
|
19126
|
+
|
|
18805
19127
|
// src/node/kintoneMetadata.ts
|
|
18806
19128
|
var KINTONE_METADATA_RESOURCES = [
|
|
18807
19129
|
"app",
|
|
@@ -18994,7 +19316,6 @@ var KintoneApiError = class extends Error {
|
|
|
18994
19316
|
this.name = "KintoneApiError";
|
|
18995
19317
|
}
|
|
18996
19318
|
};
|
|
18997
|
-
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
18998
19319
|
function createNodeKintoneConnection(baseUrl, tokenResolver) {
|
|
18999
19320
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
19000
19321
|
const apiBasePath = tokenResolver.guestSpaceId && tokenResolver.guestSpaceId > 0 ? `/k/guest/${tokenResolver.guestSpaceId}/v1` : "/k/v1";
|
|
@@ -19052,10 +19373,9 @@ function createNodeKintoneConnection(baseUrl, tokenResolver) {
|
|
|
19052
19373
|
if (tokenResolver.debug) {
|
|
19053
19374
|
tokenResolver.log?.(`[debug] response status=${res.status}`);
|
|
19054
19375
|
}
|
|
19055
|
-
const warning = res.headers.get("X-Cybozu-Warning") ?? "";
|
|
19056
19376
|
return {
|
|
19057
19377
|
response: res,
|
|
19058
|
-
searchAborted:
|
|
19378
|
+
searchAborted: isSearchAbortedWarning(res.headers.get("X-Cybozu-Warning"))
|
|
19059
19379
|
};
|
|
19060
19380
|
}
|
|
19061
19381
|
async function requestJsonResponse(path, init, appIdForToken) {
|