@rex0220/kintone-sql-tools 2.8.0 → 2.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 +375 -270
- package/dist-mcp/ksql-mcp.js +376 -271
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -31362,6 +31362,9 @@ function isJapanese(cp) {
|
|
|
31362
31362
|
return cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
|
|
31363
31363
|
}
|
|
31364
31364
|
|
|
31365
|
+
// src/types/ast.ts
|
|
31366
|
+
var NO_FROM_CTE_NAME = "__NO_FROM__";
|
|
31367
|
+
|
|
31365
31368
|
// src/parser/parser.ts
|
|
31366
31369
|
var MAX_BATCH_STATEMENTS = 20;
|
|
31367
31370
|
var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -31875,7 +31878,7 @@ var Parser = class {
|
|
|
31875
31878
|
const distinct = this.consume("DISTINCT" /* DISTINCT */);
|
|
31876
31879
|
const columns = this.parseSelectColumns();
|
|
31877
31880
|
const hasFrom = this.consume("FROM" /* FROM */);
|
|
31878
|
-
const from = hasFrom ? this.parseTableRef() : { appId: 0, alias: null, cteName:
|
|
31881
|
+
const from = hasFrom ? this.parseTableRef() : { appId: 0, alias: null, cteName: NO_FROM_CTE_NAME };
|
|
31879
31882
|
const joins = hasFrom ? this.parseJoins() : [];
|
|
31880
31883
|
const where = this.consume("WHERE" /* WHERE */) ? this.parseWhereExpr() : null;
|
|
31881
31884
|
let groupBy = [];
|
|
@@ -33263,7 +33266,7 @@ function hasWhereClause(stmt) {
|
|
|
33263
33266
|
function isNoFromSelectStatement(stmt) {
|
|
33264
33267
|
if (!stmt || typeof stmt !== "object") return false;
|
|
33265
33268
|
const obj = stmt;
|
|
33266
|
-
return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName ===
|
|
33269
|
+
return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName === NO_FROM_CTE_NAME;
|
|
33267
33270
|
}
|
|
33268
33271
|
function getInsertValuesCount(stmt) {
|
|
33269
33272
|
if (!stmt || typeof stmt !== "object") return null;
|
|
@@ -34017,6 +34020,235 @@ function isAggregateSyntheticName(name) {
|
|
|
34017
34020
|
return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
|
|
34018
34021
|
}
|
|
34019
34022
|
|
|
34023
|
+
// src/core/cteInlining.ts
|
|
34024
|
+
function canInlineSingleCte(stmt) {
|
|
34025
|
+
if (stmt.ctes.length !== 1) return false;
|
|
34026
|
+
const cteDef = stmt.ctes[0];
|
|
34027
|
+
if (cteDef.query.type !== "SELECT" || resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
|
|
34028
|
+
const finalQuery = stmt.query;
|
|
34029
|
+
if (finalQuery.type !== "SELECT") return false;
|
|
34030
|
+
if (finalQuery.from.cteName !== cteDef.name || finalQuery.joins.length > 0) return false;
|
|
34031
|
+
if (finalQuery.groupBy.length > 0 || finalQuery.distinct) return false;
|
|
34032
|
+
return !finalQuery.columns.some(
|
|
34033
|
+
(column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
|
|
34034
|
+
);
|
|
34035
|
+
}
|
|
34036
|
+
function buildInlinedQuery(stmt) {
|
|
34037
|
+
const cteBody = stmt.ctes[0].query;
|
|
34038
|
+
const final = stmt.query;
|
|
34039
|
+
const finalWhere = stripCteAlias(final.where, final.from.alias);
|
|
34040
|
+
const where = cteBody.where === null ? finalWhere : finalWhere === null ? cteBody.where : { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
|
|
34041
|
+
const columns = final.columns.every((column) => column.type === "WILDCARD") ? cteBody.columns : final.columns;
|
|
34042
|
+
return {
|
|
34043
|
+
type: "SELECT",
|
|
34044
|
+
from: cteBody.from,
|
|
34045
|
+
joins: [],
|
|
34046
|
+
columns,
|
|
34047
|
+
where,
|
|
34048
|
+
groupBy: [],
|
|
34049
|
+
having: null,
|
|
34050
|
+
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
34051
|
+
limit: final.limit ?? cteBody.limit,
|
|
34052
|
+
offset: final.offset ?? cteBody.offset,
|
|
34053
|
+
distinct: false
|
|
34054
|
+
};
|
|
34055
|
+
}
|
|
34056
|
+
function stripCteAlias(where, alias) {
|
|
34057
|
+
if (where === null || alias === null) return where;
|
|
34058
|
+
switch (where.type) {
|
|
34059
|
+
case "BINARY":
|
|
34060
|
+
return { ...where, left: stripCteAliasFromFieldValue(where.left, alias) };
|
|
34061
|
+
case "NULL_CHECK":
|
|
34062
|
+
return { ...where, field: stripCteAliasFromFieldValue(where.field, alias) };
|
|
34063
|
+
case "LOGICAL":
|
|
34064
|
+
return {
|
|
34065
|
+
...where,
|
|
34066
|
+
left: stripCteAlias(where.left, alias),
|
|
34067
|
+
right: stripCteAlias(where.right, alias)
|
|
34068
|
+
};
|
|
34069
|
+
case "NOT":
|
|
34070
|
+
case "GROUP":
|
|
34071
|
+
return { ...where, expr: stripCteAlias(where.expr, alias) };
|
|
34072
|
+
case "EXISTS":
|
|
34073
|
+
return where;
|
|
34074
|
+
}
|
|
34075
|
+
}
|
|
34076
|
+
function stripCteAliasFromFieldValue(value, alias) {
|
|
34077
|
+
if (value.type === "FIELD" && value.tableAlias === alias) {
|
|
34078
|
+
return { ...value, tableAlias: null };
|
|
34079
|
+
}
|
|
34080
|
+
return value;
|
|
34081
|
+
}
|
|
34082
|
+
|
|
34083
|
+
// src/core/optimization/wherePredicatePushdown.ts
|
|
34084
|
+
function extractSafePushdownLeaves(where, options = {}) {
|
|
34085
|
+
return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
|
|
34086
|
+
}
|
|
34087
|
+
function extractTypedPushdownCandidates(where, options = {}) {
|
|
34088
|
+
return extractAndLeaves(
|
|
34089
|
+
where,
|
|
34090
|
+
(expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
|
|
34091
|
+
);
|
|
34092
|
+
}
|
|
34093
|
+
function extractAndLeaves(where, accept) {
|
|
34094
|
+
switch (where.type) {
|
|
34095
|
+
case "BINARY":
|
|
34096
|
+
return accept(where) ? where : null;
|
|
34097
|
+
case "LOGICAL":
|
|
34098
|
+
if (where.op !== "AND") return null;
|
|
34099
|
+
{
|
|
34100
|
+
const left = extractAndLeaves(where.left, accept);
|
|
34101
|
+
const right = extractAndLeaves(where.right, accept);
|
|
34102
|
+
if (left && right) return { ...where, left, right };
|
|
34103
|
+
return left ?? right ?? null;
|
|
34104
|
+
}
|
|
34105
|
+
case "GROUP":
|
|
34106
|
+
return extractAndLeaves(where.expr, accept);
|
|
34107
|
+
case "NULL_CHECK":
|
|
34108
|
+
case "NOT":
|
|
34109
|
+
case "EXISTS":
|
|
34110
|
+
return null;
|
|
34111
|
+
}
|
|
34112
|
+
}
|
|
34113
|
+
function isSafeComparison(expr, options) {
|
|
34114
|
+
if (isKlikeComparison(expr, options)) return true;
|
|
34115
|
+
if (isSafeIdComparison(expr, options)) return true;
|
|
34116
|
+
if (isNumericCandidate(expr, options)) {
|
|
34117
|
+
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
34118
|
+
}
|
|
34119
|
+
return isSelectionInComparison(expr, options);
|
|
34120
|
+
}
|
|
34121
|
+
function isKlikeComparison(expr, options) {
|
|
34122
|
+
if (options.allowKlike === false) return false;
|
|
34123
|
+
if (expr.op !== "KLIKE" && expr.op !== "NOT_KLIKE") return false;
|
|
34124
|
+
if (expr.left.type !== "FIELD" || !isTargetField(expr.left, options)) return false;
|
|
34125
|
+
return expr.right.type === "STRING" || options.allowUnresolvedKlikeVariables === true && expr.right.type === "VARIABLE";
|
|
34126
|
+
}
|
|
34127
|
+
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
34128
|
+
"DROP_DOWN",
|
|
34129
|
+
"RADIO_BUTTON",
|
|
34130
|
+
"CHECK_BOX",
|
|
34131
|
+
"MULTI_SELECT",
|
|
34132
|
+
"STATUS"
|
|
34133
|
+
]);
|
|
34134
|
+
function isSelectionInComparison(expr, options) {
|
|
34135
|
+
if (!isSelectionInCandidate(expr, options)) return false;
|
|
34136
|
+
if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
|
|
34137
|
+
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
34138
|
+
if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
|
|
34139
|
+
const validOptions = options.fieldOptions?.get(expr.left.field);
|
|
34140
|
+
if (validOptions === void 0) return false;
|
|
34141
|
+
return expr.right.values.every(
|
|
34142
|
+
(value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
|
|
34143
|
+
);
|
|
34144
|
+
}
|
|
34145
|
+
function isSafeIdComparison(expr, options) {
|
|
34146
|
+
if (!isTargetIdField(expr.left, options)) return false;
|
|
34147
|
+
if (expr.right.type !== "NUMBER") return false;
|
|
34148
|
+
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
34149
|
+
}
|
|
34150
|
+
function isTargetIdField(field, options) {
|
|
34151
|
+
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
34152
|
+
const targetAlias = options.tableAlias ?? null;
|
|
34153
|
+
if (field.tableAlias === targetAlias) return true;
|
|
34154
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
34155
|
+
}
|
|
34156
|
+
function isNumericCandidate(expr, options) {
|
|
34157
|
+
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
34158
|
+
if (!isTargetField(expr.left, options)) return false;
|
|
34159
|
+
if (expr.right.type !== "NUMBER") return false;
|
|
34160
|
+
if (expr.op === "=") return true;
|
|
34161
|
+
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
34162
|
+
}
|
|
34163
|
+
function isSelectionInCandidate(expr, options) {
|
|
34164
|
+
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
34165
|
+
if (!isTargetField(expr.left, options)) return false;
|
|
34166
|
+
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
34167
|
+
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
34168
|
+
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
34169
|
+
}
|
|
34170
|
+
function isTargetField(field, options) {
|
|
34171
|
+
const targetAlias = options.tableAlias ?? null;
|
|
34172
|
+
if (field.tableAlias === targetAlias) return true;
|
|
34173
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
34174
|
+
}
|
|
34175
|
+
|
|
34176
|
+
// src/core/optimization/klikePushdownPlan.ts
|
|
34177
|
+
function buildKlikePushdownPlan(stmt, options = {}) {
|
|
34178
|
+
const joinsAreSafeForKlike = stmt.joins.every((join) => join.type === "INNER");
|
|
34179
|
+
const common = {
|
|
34180
|
+
allowKlike: joinsAreSafeForKlike,
|
|
34181
|
+
allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
|
|
34182
|
+
};
|
|
34183
|
+
let mainCondition = null;
|
|
34184
|
+
if (stmt.where !== null && !stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
34185
|
+
if (stmt.joins.length === 0) {
|
|
34186
|
+
mainCondition = extractSafePushdownLeaves(stmt.where, {
|
|
34187
|
+
...common,
|
|
34188
|
+
tableAlias: stmt.from.alias ?? void 0,
|
|
34189
|
+
allowUnqualifiedFields: true,
|
|
34190
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
34191
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
34192
|
+
});
|
|
34193
|
+
} else if (stmt.from.alias) {
|
|
34194
|
+
mainCondition = extractSafePushdownLeaves(stmt.where, {
|
|
34195
|
+
...common,
|
|
34196
|
+
tableAlias: stmt.from.alias,
|
|
34197
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
34198
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
34199
|
+
});
|
|
34200
|
+
}
|
|
34201
|
+
}
|
|
34202
|
+
const joinConditions = /* @__PURE__ */ new Map();
|
|
34203
|
+
if (stmt.where !== null) {
|
|
34204
|
+
for (const join of stmt.joins) {
|
|
34205
|
+
if (!join.table.alias || join.table.subtableCode || join.table.cteName !== null) continue;
|
|
34206
|
+
const condition = extractSafePushdownLeaves(stmt.where, {
|
|
34207
|
+
...common,
|
|
34208
|
+
tableAlias: join.table.alias,
|
|
34209
|
+
fieldTypes: options.fieldTypesByApp?.get(join.table.appId),
|
|
34210
|
+
fieldOptions: options.fieldOptionsByApp?.get(join.table.appId)
|
|
34211
|
+
});
|
|
34212
|
+
if (condition !== null) joinConditions.set(join.table.alias, condition);
|
|
34213
|
+
}
|
|
34214
|
+
}
|
|
34215
|
+
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
34216
|
+
collectKlikes(mainCondition, appliedKlikes);
|
|
34217
|
+
for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
|
|
34218
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
34219
|
+
collectKlikes(stmt.where, allKlikes);
|
|
34220
|
+
return {
|
|
34221
|
+
mainCondition,
|
|
34222
|
+
joinConditions,
|
|
34223
|
+
appliedKlikes,
|
|
34224
|
+
allKlikes: [...allKlikes]
|
|
34225
|
+
};
|
|
34226
|
+
}
|
|
34227
|
+
function unappliedKlikes(plan) {
|
|
34228
|
+
return plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr));
|
|
34229
|
+
}
|
|
34230
|
+
function collectKlikes(where, out) {
|
|
34231
|
+
if (where === null) return;
|
|
34232
|
+
if (isKlike(where)) {
|
|
34233
|
+
out.add(where);
|
|
34234
|
+
return;
|
|
34235
|
+
}
|
|
34236
|
+
switch (where.type) {
|
|
34237
|
+
case "LOGICAL":
|
|
34238
|
+
collectKlikes(where.left, out);
|
|
34239
|
+
collectKlikes(where.right, out);
|
|
34240
|
+
return;
|
|
34241
|
+
case "NOT":
|
|
34242
|
+
case "GROUP":
|
|
34243
|
+
collectKlikes(where.expr, out);
|
|
34244
|
+
return;
|
|
34245
|
+
case "BINARY":
|
|
34246
|
+
case "NULL_CHECK":
|
|
34247
|
+
case "EXISTS":
|
|
34248
|
+
return;
|
|
34249
|
+
}
|
|
34250
|
+
}
|
|
34251
|
+
|
|
34020
34252
|
// src/core/klikeValidation.ts
|
|
34021
34253
|
var KlikeValidationError = class extends Error {
|
|
34022
34254
|
constructor(message) {
|
|
@@ -34027,6 +34259,13 @@ var KlikeValidationError = class extends Error {
|
|
|
34027
34259
|
function validateKlikeStatement(stmt) {
|
|
34028
34260
|
validateStatement(stmt);
|
|
34029
34261
|
}
|
|
34262
|
+
function validateKlikePushdownPlan(plan) {
|
|
34263
|
+
if (unappliedKlikes(plan).length > 0) {
|
|
34264
|
+
throw new KlikeValidationError(
|
|
34265
|
+
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u3092\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u307E\u305B\u3093\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
34266
|
+
);
|
|
34267
|
+
}
|
|
34268
|
+
}
|
|
34030
34269
|
function validateStatement(stmt) {
|
|
34031
34270
|
switch (stmt.type) {
|
|
34032
34271
|
case "SELECT":
|
|
@@ -34058,7 +34297,7 @@ function validateStatement(stmt) {
|
|
|
34058
34297
|
case "REORDER":
|
|
34059
34298
|
if (containsKlike(stmt)) {
|
|
34060
34299
|
throw new KlikeValidationError(
|
|
34061
|
-
"KLIKE / NOT KLIKE \u306F
|
|
34300
|
+
"KLIKE / NOT KLIKE \u306F\u5168 DML\uFF08UPDATE / DELETE / INSERT / UPSERT / REORDER\uFF09\u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
34062
34301
|
);
|
|
34063
34302
|
}
|
|
34064
34303
|
return;
|
|
@@ -34078,9 +34317,8 @@ function validateUnion(stmt) {
|
|
|
34078
34317
|
validateSelect(stmt.right);
|
|
34079
34318
|
}
|
|
34080
34319
|
function validateWith(stmt) {
|
|
34081
|
-
|
|
34082
|
-
|
|
34083
|
-
validateSelect(inlined);
|
|
34320
|
+
if (canInlineSingleCte(stmt)) {
|
|
34321
|
+
validateSelect(buildInlinedQuery(stmt));
|
|
34084
34322
|
return;
|
|
34085
34323
|
}
|
|
34086
34324
|
for (const cte of stmt.ctes) {
|
|
@@ -34092,10 +34330,15 @@ function validateWith(stmt) {
|
|
|
34092
34330
|
function validateSelect(stmt) {
|
|
34093
34331
|
validateOwnKlikeExpressions(stmt);
|
|
34094
34332
|
if (whereHasKlike(stmt.where)) {
|
|
34095
|
-
const
|
|
34096
|
-
if (
|
|
34333
|
+
const directKintoneSimple = resolveSelectMode(stmt) === "SIMPLE" && stmt.from.cteName === null && stmt.joins.every((join) => join.table.cteName === null);
|
|
34334
|
+
if (!directKintoneSimple) {
|
|
34335
|
+
const plan = buildKlikePushdownPlan(stmt, { allowUnresolvedVariables: true });
|
|
34336
|
+
if (unappliedKlikes(plan).length === 0) {
|
|
34337
|
+
validateNestedSelects(stmt);
|
|
34338
|
+
return;
|
|
34339
|
+
}
|
|
34097
34340
|
throw new KlikeValidationError(
|
|
34098
|
-
"KLIKE / NOT KLIKE \u306F
|
|
34341
|
+
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u306F\u3001\u7269\u7406\u30C6\u30FC\u30D6\u30EB\u306B\u5BFE\u3059\u308B AND \u30EA\u30FC\u30D5\u3068\u3057\u3066\u5FC5\u305A\u62BC\u3057\u4E0B\u3052\u3089\u308C\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
34099
34342
|
);
|
|
34100
34343
|
}
|
|
34101
34344
|
}
|
|
@@ -34125,33 +34368,6 @@ function validateNestedSelects(node) {
|
|
|
34125
34368
|
if (obj.type === "SELECT") validateSelect(obj);
|
|
34126
34369
|
}, true);
|
|
34127
34370
|
}
|
|
34128
|
-
function buildEffectiveInlineSelect(stmt) {
|
|
34129
|
-
if (stmt.ctes.length !== 1) return null;
|
|
34130
|
-
const cte = stmt.ctes[0];
|
|
34131
|
-
if (cte.query.type !== "SELECT" || resolveSelectMode(cte.query) !== "SIMPLE") return null;
|
|
34132
|
-
if (stmt.query.type !== "SELECT") return null;
|
|
34133
|
-
const final = stmt.query;
|
|
34134
|
-
if (final.from.cteName !== cte.name || final.joins.length > 0) return null;
|
|
34135
|
-
if (final.groupBy.length > 0 || final.distinct) return null;
|
|
34136
|
-
if (final.columns.some(
|
|
34137
|
-
(column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
|
|
34138
|
-
)) return null;
|
|
34139
|
-
const where = cte.query.where === null ? final.where : final.where === null ? cte.query.where : { type: "LOGICAL", op: "AND", left: cte.query.where, right: final.where };
|
|
34140
|
-
const columns = final.columns.every((column) => column.type === "WILDCARD") ? cte.query.columns : final.columns;
|
|
34141
|
-
return {
|
|
34142
|
-
type: "SELECT",
|
|
34143
|
-
from: cte.query.from,
|
|
34144
|
-
joins: [],
|
|
34145
|
-
columns,
|
|
34146
|
-
where,
|
|
34147
|
-
groupBy: [],
|
|
34148
|
-
having: null,
|
|
34149
|
-
orderBy: final.orderBy.length > 0 ? final.orderBy : cte.query.orderBy,
|
|
34150
|
-
limit: final.limit ?? cte.query.limit,
|
|
34151
|
-
offset: final.offset ?? cte.query.offset,
|
|
34152
|
-
distinct: false
|
|
34153
|
-
};
|
|
34154
|
-
}
|
|
34155
34371
|
function containsKlike(node) {
|
|
34156
34372
|
let found = false;
|
|
34157
34373
|
walkObjects(node, (obj) => {
|
|
@@ -34656,25 +34872,29 @@ function resolveFieldRef(row, field) {
|
|
|
34656
34872
|
}
|
|
34657
34873
|
|
|
34658
34874
|
// src/engine/evalWhere.ts
|
|
34659
|
-
function evalWhere(expr, row, resolveFieldType) {
|
|
34875
|
+
function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
|
|
34660
34876
|
switch (expr.type) {
|
|
34661
34877
|
case "BINARY":
|
|
34662
|
-
return evalBinary(expr, row, resolveFieldType);
|
|
34878
|
+
return evalBinary(expr, row, resolveFieldType, appliedKlikes);
|
|
34663
34879
|
case "NULL_CHECK":
|
|
34664
34880
|
return evalNullCheck(expr, row);
|
|
34665
34881
|
case "LOGICAL":
|
|
34666
|
-
return evalLogical(expr, row, resolveFieldType);
|
|
34882
|
+
return evalLogical(expr, row, resolveFieldType, appliedKlikes);
|
|
34667
34883
|
case "NOT":
|
|
34668
|
-
return !evalWhere(expr.expr, row, resolveFieldType);
|
|
34884
|
+
return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
34669
34885
|
case "GROUP":
|
|
34670
|
-
return evalWhere(expr.expr, row, resolveFieldType);
|
|
34886
|
+
return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
34671
34887
|
case "EXISTS": {
|
|
34672
34888
|
const exists = expr.resolved;
|
|
34673
34889
|
return expr.not ? !exists : exists;
|
|
34674
34890
|
}
|
|
34675
34891
|
}
|
|
34676
34892
|
}
|
|
34677
|
-
function evalBinary(expr, row, resolveFieldType) {
|
|
34893
|
+
function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
|
|
34894
|
+
if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
|
|
34895
|
+
if (appliedKlikes?.has(expr)) return true;
|
|
34896
|
+
throw new Error("KLIKE / NOT KLIKE \u306F\u62BC\u3057\u4E0B\u3052\u6E08\u307F\u96C6\u5408\u306B\u542B\u307E\u308C\u306A\u3044\u305F\u3081 JavaScript \u5074\u3067\u306F\u8A55\u4FA1\u3067\u304D\u307E\u305B\u3093");
|
|
34897
|
+
}
|
|
34678
34898
|
const left = resolveField(expr.left, row, resolveFieldType);
|
|
34679
34899
|
const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
|
|
34680
34900
|
return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
|
|
@@ -34756,11 +34976,11 @@ function evalNullCheck(expr, row) {
|
|
|
34756
34976
|
const val = resolveField(expr.field, row);
|
|
34757
34977
|
return expr.not ? val !== "" : val === "";
|
|
34758
34978
|
}
|
|
34759
|
-
function evalLogical(expr, row, resolveFieldType) {
|
|
34979
|
+
function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
|
|
34760
34980
|
if (expr.op === "AND") {
|
|
34761
|
-
return evalWhere(expr.left, row, resolveFieldType) && evalWhere(expr.right, row, resolveFieldType);
|
|
34981
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
34762
34982
|
}
|
|
34763
|
-
return evalWhere(expr.left, row, resolveFieldType) || evalWhere(expr.right, row, resolveFieldType);
|
|
34983
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
34764
34984
|
}
|
|
34765
34985
|
function resolveField(field, row, resolveFieldType) {
|
|
34766
34986
|
if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
|
|
@@ -34861,7 +35081,7 @@ function matchLike(value, pattern) {
|
|
|
34861
35081
|
function assertDmlWhereIsSafe(where) {
|
|
34862
35082
|
if (whereHasKlike(where)) {
|
|
34863
35083
|
throw new DmlConvertError(
|
|
34864
|
-
"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\
|
|
35084
|
+
"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"
|
|
34865
35085
|
);
|
|
34866
35086
|
}
|
|
34867
35087
|
if (!whereHasLike(where)) return;
|
|
@@ -35203,6 +35423,7 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
35203
35423
|
let windowOffset = 0;
|
|
35204
35424
|
const cursorQuery0 = buildCursorQuery(query, cursorId);
|
|
35205
35425
|
const first = await fetchPage(fetcher, app, cursorQuery0, fetchFields, pageSize, windowOffset);
|
|
35426
|
+
notifySearchAborted(first, options);
|
|
35206
35427
|
allRecords.push(...first.records);
|
|
35207
35428
|
if (allRecords.length > maxRecords2) {
|
|
35208
35429
|
if (onLimit2 === "truncate") {
|
|
@@ -35246,6 +35467,7 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
35246
35467
|
(offset) => fetchPage(fetcher, app, cq, fetchFields, pageSize, offset)
|
|
35247
35468
|
)
|
|
35248
35469
|
);
|
|
35470
|
+
for (const response of responses) notifySearchAborted(response, options);
|
|
35249
35471
|
let done = false;
|
|
35250
35472
|
for (const res of responses) {
|
|
35251
35473
|
allRecords.push(...res.records);
|
|
@@ -35274,6 +35496,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
|
35274
35496
|
}
|
|
35275
35497
|
return allRecords;
|
|
35276
35498
|
}
|
|
35499
|
+
function notifySearchAborted(response, options) {
|
|
35500
|
+
if (response.searchAborted) options.onSearchAborted?.();
|
|
35501
|
+
}
|
|
35277
35502
|
function extractIds(records) {
|
|
35278
35503
|
return records.map((r) => {
|
|
35279
35504
|
const raw = r["$id"]?.value;
|
|
@@ -35330,7 +35555,8 @@ async function fetchRecordsForSharedPlan(getRecords, app, query, fields, options
|
|
|
35330
35555
|
maxRecords: options.maxRecords,
|
|
35331
35556
|
parallel: options.parallel,
|
|
35332
35557
|
onLimit: options.onLimit ?? "error",
|
|
35333
|
-
onTruncate: options.onTruncate
|
|
35558
|
+
onTruncate: options.onTruncate,
|
|
35559
|
+
onSearchAborted: options.onSearchAborted
|
|
35334
35560
|
});
|
|
35335
35561
|
return {
|
|
35336
35562
|
records,
|
|
@@ -35351,92 +35577,6 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
35351
35577
|
};
|
|
35352
35578
|
}
|
|
35353
35579
|
|
|
35354
|
-
// src/core/optimization/wherePredicatePushdown.ts
|
|
35355
|
-
function extractSafePushdownLeaves(where, options = {}) {
|
|
35356
|
-
return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
|
|
35357
|
-
}
|
|
35358
|
-
function extractTypedPushdownCandidates(where, options = {}) {
|
|
35359
|
-
return extractAndLeaves(
|
|
35360
|
-
where,
|
|
35361
|
-
(expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
|
|
35362
|
-
);
|
|
35363
|
-
}
|
|
35364
|
-
function extractAndLeaves(where, accept) {
|
|
35365
|
-
switch (where.type) {
|
|
35366
|
-
case "BINARY":
|
|
35367
|
-
return accept(where) ? where : null;
|
|
35368
|
-
case "LOGICAL":
|
|
35369
|
-
if (where.op !== "AND") return null;
|
|
35370
|
-
{
|
|
35371
|
-
const left = extractAndLeaves(where.left, accept);
|
|
35372
|
-
const right = extractAndLeaves(where.right, accept);
|
|
35373
|
-
if (left && right) return { ...where, left, right };
|
|
35374
|
-
return left ?? right ?? null;
|
|
35375
|
-
}
|
|
35376
|
-
case "GROUP":
|
|
35377
|
-
return extractAndLeaves(where.expr, accept);
|
|
35378
|
-
case "NULL_CHECK":
|
|
35379
|
-
case "NOT":
|
|
35380
|
-
case "EXISTS":
|
|
35381
|
-
return null;
|
|
35382
|
-
}
|
|
35383
|
-
}
|
|
35384
|
-
function isSafeComparison(expr, options) {
|
|
35385
|
-
if (isSafeIdComparison(expr, options)) return true;
|
|
35386
|
-
if (isNumericCandidate(expr, options)) {
|
|
35387
|
-
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
35388
|
-
}
|
|
35389
|
-
return isSelectionInComparison(expr, options);
|
|
35390
|
-
}
|
|
35391
|
-
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
35392
|
-
"DROP_DOWN",
|
|
35393
|
-
"RADIO_BUTTON",
|
|
35394
|
-
"CHECK_BOX",
|
|
35395
|
-
"MULTI_SELECT",
|
|
35396
|
-
"STATUS"
|
|
35397
|
-
]);
|
|
35398
|
-
function isSelectionInComparison(expr, options) {
|
|
35399
|
-
if (!isSelectionInCandidate(expr, options)) return false;
|
|
35400
|
-
if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
|
|
35401
|
-
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
35402
|
-
if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
|
|
35403
|
-
const validOptions = options.fieldOptions?.get(expr.left.field);
|
|
35404
|
-
if (validOptions === void 0) return false;
|
|
35405
|
-
return expr.right.values.every(
|
|
35406
|
-
(value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
|
|
35407
|
-
);
|
|
35408
|
-
}
|
|
35409
|
-
function isSafeIdComparison(expr, options) {
|
|
35410
|
-
if (!isTargetIdField(expr.left, options)) return false;
|
|
35411
|
-
if (expr.right.type !== "NUMBER") return false;
|
|
35412
|
-
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
35413
|
-
}
|
|
35414
|
-
function isTargetIdField(field, options) {
|
|
35415
|
-
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
35416
|
-
const targetAlias = options.tableAlias ?? null;
|
|
35417
|
-
if (field.tableAlias === targetAlias) return true;
|
|
35418
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
35419
|
-
}
|
|
35420
|
-
function isNumericCandidate(expr, options) {
|
|
35421
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
35422
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
35423
|
-
if (expr.right.type !== "NUMBER") return false;
|
|
35424
|
-
if (expr.op === "=") return true;
|
|
35425
|
-
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
35426
|
-
}
|
|
35427
|
-
function isSelectionInCandidate(expr, options) {
|
|
35428
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
35429
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
35430
|
-
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
35431
|
-
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
35432
|
-
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
35433
|
-
}
|
|
35434
|
-
function isTargetField(field, options) {
|
|
35435
|
-
const targetAlias = options.tableAlias ?? null;
|
|
35436
|
-
if (field.tableAlias === targetAlias) return true;
|
|
35437
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
35438
|
-
}
|
|
35439
|
-
|
|
35440
35580
|
// src/engine/process.ts
|
|
35441
35581
|
function flatten(record2, alias) {
|
|
35442
35582
|
const row = {};
|
|
@@ -35501,9 +35641,9 @@ function applyJoin(leftRows, rightRows, join) {
|
|
|
35501
35641
|
}
|
|
35502
35642
|
return result;
|
|
35503
35643
|
}
|
|
35504
|
-
function applyFilter(rows, where, resolveFieldType) {
|
|
35644
|
+
function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
|
|
35505
35645
|
if (where === null) return rows;
|
|
35506
|
-
return rows.filter((row) => evalWhere(where, row, resolveFieldType));
|
|
35646
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
|
|
35507
35647
|
}
|
|
35508
35648
|
function hasAggregateColumns(columns) {
|
|
35509
35649
|
return columns.some(
|
|
@@ -35960,7 +36100,8 @@ function runFullScan(input) {
|
|
|
35960
36100
|
optionOrders,
|
|
35961
36101
|
sortKinds,
|
|
35962
36102
|
fieldTypeResolver,
|
|
35963
|
-
havingFieldTypeResolver
|
|
36103
|
+
havingFieldTypeResolver,
|
|
36104
|
+
appliedKlikes
|
|
35964
36105
|
} = input;
|
|
35965
36106
|
let rows = [];
|
|
35966
36107
|
const mainAlias = stmt.from.alias;
|
|
@@ -35972,7 +36113,7 @@ function runFullScan(input) {
|
|
|
35972
36113
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
35973
36114
|
rows = applyJoin(rows, rightRows, join);
|
|
35974
36115
|
}
|
|
35975
|
-
rows = applyFilter(rows, stmt.where, fieldTypeResolver);
|
|
36116
|
+
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
|
|
35976
36117
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
35977
36118
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
|
|
35978
36119
|
}
|
|
@@ -36028,13 +36169,32 @@ function toFlatString(value) {
|
|
|
36028
36169
|
}
|
|
36029
36170
|
|
|
36030
36171
|
// src/execute.ts
|
|
36172
|
+
var SEARCH_ABORTED_WARNING = "\u691C\u7D22\u304C 10 \u4E07\u4EF6\u3067\u6253\u3061\u5207\u3089\u308C\u3001\u7D50\u679C\u304C\u6B20\u843D\u3057\u305F\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002";
|
|
36173
|
+
var SearchAbortedError = class extends Error {
|
|
36174
|
+
constructor() {
|
|
36175
|
+
super("SearchAbortedError: kintone \u306E\u691C\u7D22\u304C 10 \u4E07\u4EF6\u3067\u6253\u3061\u5207\u3089\u308C\u305F\u305F\u3081\u3001\u5B8C\u5168\u306A\u5BFE\u8C61\u96C6\u5408\u3092\u78BA\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002");
|
|
36176
|
+
this.name = "SearchAbortedError";
|
|
36177
|
+
}
|
|
36178
|
+
};
|
|
36031
36179
|
async function execute(sql, client, options = {}) {
|
|
36180
|
+
const startedAt = Date.now();
|
|
36181
|
+
const stmt = parseSql(sql);
|
|
36032
36182
|
const metrics = createEmptyMetrics();
|
|
36033
36183
|
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
36034
|
-
const
|
|
36035
|
-
const
|
|
36184
|
+
const collector = { aborted: false };
|
|
36185
|
+
const guardedClient = wrapClientWithSearchAbort(
|
|
36186
|
+
countedClient,
|
|
36187
|
+
collector,
|
|
36188
|
+
!isSelectLikeStatement(stmt)
|
|
36189
|
+
);
|
|
36190
|
+
const result = await executeParsedStatement(
|
|
36191
|
+
stmt,
|
|
36192
|
+
guardedClient,
|
|
36193
|
+
options,
|
|
36194
|
+
options.cacheContext ?? "default"
|
|
36195
|
+
);
|
|
36036
36196
|
metrics.elapsedMs = Date.now() - startedAt;
|
|
36037
|
-
return { ...result, metrics };
|
|
36197
|
+
return { ...attachSearchAbortWarning(result, collector), metrics };
|
|
36038
36198
|
}
|
|
36039
36199
|
function createEmptyMetrics() {
|
|
36040
36200
|
return {
|
|
@@ -36083,10 +36243,27 @@ function wrapClientWithMetrics(client, metrics) {
|
|
|
36083
36243
|
}
|
|
36084
36244
|
};
|
|
36085
36245
|
}
|
|
36086
|
-
|
|
36087
|
-
|
|
36088
|
-
|
|
36089
|
-
|
|
36246
|
+
function wrapClientWithSearchAbort(client, collector, failClosed) {
|
|
36247
|
+
return {
|
|
36248
|
+
...client,
|
|
36249
|
+
getRecords: async (params) => {
|
|
36250
|
+
const response = await client.getRecords(params);
|
|
36251
|
+
if (response.searchAborted) {
|
|
36252
|
+
collector.aborted = true;
|
|
36253
|
+
if (failClosed) throw new SearchAbortedError();
|
|
36254
|
+
}
|
|
36255
|
+
return response;
|
|
36256
|
+
}
|
|
36257
|
+
};
|
|
36258
|
+
}
|
|
36259
|
+
function isSelectLikeStatement(stmt) {
|
|
36260
|
+
return stmt.type === "SELECT" || stmt.type === "UNION" || stmt.type === "WITH";
|
|
36261
|
+
}
|
|
36262
|
+
function attachSearchAbortWarning(result, collector) {
|
|
36263
|
+
if (!collector.aborted || result.type !== "SELECT") return result;
|
|
36264
|
+
const warnings = new Set(result.warnings ?? []);
|
|
36265
|
+
warnings.add(SEARCH_ABORTED_WARNING);
|
|
36266
|
+
return { ...result, warnings: [...warnings] };
|
|
36090
36267
|
}
|
|
36091
36268
|
async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
36092
36269
|
const unresolved = findVariableRef(stmt);
|
|
@@ -36199,10 +36376,19 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
36199
36376
|
targetAppId: info.targetAppId
|
|
36200
36377
|
})
|
|
36201
36378
|
} : batchOptions;
|
|
36379
|
+
const searchAbortCollector = { aborted: false };
|
|
36380
|
+
const statementClient = wrapClientWithSearchAbort(
|
|
36381
|
+
countedClient,
|
|
36382
|
+
searchAbortCollector,
|
|
36383
|
+
info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH"
|
|
36384
|
+
);
|
|
36202
36385
|
const outcome = await runWithDeadline(
|
|
36203
|
-
executeBatchStatement(statements[i], info,
|
|
36386
|
+
executeBatchStatement(statements[i], info, statementClient, stmtOptions, cacheContext, tempTables, variables),
|
|
36204
36387
|
remaining
|
|
36205
36388
|
);
|
|
36389
|
+
if (outcome.result) {
|
|
36390
|
+
outcome.result = attachSearchAbortWarning(outcome.result, searchAbortCollector);
|
|
36391
|
+
}
|
|
36206
36392
|
results.push({ ...base, status: "success", ...outcome });
|
|
36207
36393
|
} catch (e) {
|
|
36208
36394
|
results.push({ ...base, status: "error", error: toBatchStatementError(e) });
|
|
@@ -36533,7 +36719,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache) {
|
|
|
36533
36719
|
}
|
|
36534
36720
|
}
|
|
36535
36721
|
function isNoFromSelect(stmt) {
|
|
36536
|
-
return stmt.from.appId === 0 && stmt.from.cteName ===
|
|
36722
|
+
return stmt.from.appId === 0 && stmt.from.cteName === NO_FROM_CTE_NAME;
|
|
36537
36723
|
}
|
|
36538
36724
|
function arithHasFieldRef(node) {
|
|
36539
36725
|
if (node.type === "FIELD_REF") return true;
|
|
@@ -36660,23 +36846,6 @@ async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
|
|
|
36660
36846
|
}
|
|
36661
36847
|
}
|
|
36662
36848
|
}
|
|
36663
|
-
function extractMainSafePushdown(stmt, fieldTypes, fieldOptions) {
|
|
36664
|
-
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
36665
|
-
if (stmt.joins.length === 0) {
|
|
36666
|
-
return extractSafePushdownLeaves(stmt.where, {
|
|
36667
|
-
tableAlias: stmt.from.alias ?? void 0,
|
|
36668
|
-
allowUnqualifiedFields: true,
|
|
36669
|
-
fieldTypes,
|
|
36670
|
-
fieldOptions
|
|
36671
|
-
});
|
|
36672
|
-
}
|
|
36673
|
-
if (!stmt.from.alias) return null;
|
|
36674
|
-
return extractSafePushdownLeaves(stmt.where, {
|
|
36675
|
-
tableAlias: stmt.from.alias,
|
|
36676
|
-
fieldTypes,
|
|
36677
|
-
fieldOptions
|
|
36678
|
-
});
|
|
36679
|
-
}
|
|
36680
36849
|
function extractMainTypedPushdownCandidate(stmt) {
|
|
36681
36850
|
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
36682
36851
|
if (stmt.joins.length === 0) {
|
|
@@ -36858,23 +37027,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
36858
37027
|
loadTypedInFieldTypes(stmt, client, cacheContext)
|
|
36859
37028
|
]);
|
|
36860
37029
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
36861
|
-
const
|
|
36862
|
-
|
|
36863
|
-
|
|
36864
|
-
|
|
36865
|
-
);
|
|
36866
|
-
const tableConditions = /* @__PURE__ */ new Map();
|
|
36867
|
-
if (stmt.where !== null) {
|
|
36868
|
-
for (const join of stmt.joins) {
|
|
36869
|
-
if (!join.table.alias || join.table.subtableCode || join.table.cteName !== null) continue;
|
|
36870
|
-
const cond = extractSafePushdownLeaves(stmt.where, {
|
|
36871
|
-
tableAlias: join.table.alias,
|
|
36872
|
-
fieldTypes: pushdownMeta.fieldTypesByApp.get(join.table.appId),
|
|
36873
|
-
fieldOptions: pushdownMeta.fieldOptionsByApp.get(join.table.appId)
|
|
36874
|
-
});
|
|
36875
|
-
if (cond) tableConditions.set(join.table.alias, cond);
|
|
36876
|
-
}
|
|
36877
|
-
}
|
|
37030
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
37031
|
+
validateKlikePushdownPlan(pushdownPlan);
|
|
37032
|
+
const mainPushDown = pushdownPlan.mainCondition;
|
|
37033
|
+
const tableConditions = pushdownPlan.joinConditions;
|
|
36878
37034
|
const mainFetch = fetchTableRecordsForFullScan(
|
|
36879
37035
|
stmt,
|
|
36880
37036
|
stmt.from,
|
|
@@ -36955,7 +37111,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
36955
37111
|
optionOrders,
|
|
36956
37112
|
sortKinds,
|
|
36957
37113
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
36958
|
-
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
37114
|
+
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
37115
|
+
appliedKlikes: pushdownPlan.appliedKlikes
|
|
36959
37116
|
});
|
|
36960
37117
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
36961
37118
|
}
|
|
@@ -37004,83 +37161,6 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
|
|
|
37004
37161
|
}
|
|
37005
37162
|
return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
|
|
37006
37163
|
}
|
|
37007
|
-
function canInlineSingleCte(stmt) {
|
|
37008
|
-
if (stmt.ctes.length !== 1) return false;
|
|
37009
|
-
const cteDef = stmt.ctes[0];
|
|
37010
|
-
if (cteDef.query.type !== "SELECT") return false;
|
|
37011
|
-
if (resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
|
|
37012
|
-
const finalQuery = stmt.query;
|
|
37013
|
-
if (finalQuery.type !== "SELECT") return false;
|
|
37014
|
-
if (finalQuery.from.cteName !== cteDef.name) return false;
|
|
37015
|
-
if (finalQuery.joins.length > 0) return false;
|
|
37016
|
-
if (finalQuery.groupBy.length > 0) return false;
|
|
37017
|
-
if (finalQuery.distinct) return false;
|
|
37018
|
-
if (finalQuery.columns.some(
|
|
37019
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"
|
|
37020
|
-
)) return false;
|
|
37021
|
-
return true;
|
|
37022
|
-
}
|
|
37023
|
-
function buildInlinedQuery(stmt) {
|
|
37024
|
-
const cteBody = stmt.ctes[0].query;
|
|
37025
|
-
const final = stmt.query;
|
|
37026
|
-
const cteAlias = final.from.alias;
|
|
37027
|
-
const finalWhere = stripCteAlias(final.where, cteAlias);
|
|
37028
|
-
let mergedWhere;
|
|
37029
|
-
if (cteBody.where === null) mergedWhere = finalWhere;
|
|
37030
|
-
else if (finalWhere === null) mergedWhere = cteBody.where;
|
|
37031
|
-
else mergedWhere = { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
|
|
37032
|
-
const columns = final.columns.every((c) => c.type === "WILDCARD") ? cteBody.columns : final.columns;
|
|
37033
|
-
return {
|
|
37034
|
-
type: "SELECT",
|
|
37035
|
-
from: cteBody.from,
|
|
37036
|
-
joins: [],
|
|
37037
|
-
columns,
|
|
37038
|
-
where: mergedWhere,
|
|
37039
|
-
groupBy: [],
|
|
37040
|
-
having: null,
|
|
37041
|
-
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
37042
|
-
limit: final.limit ?? cteBody.limit,
|
|
37043
|
-
offset: final.offset ?? cteBody.offset,
|
|
37044
|
-
distinct: false
|
|
37045
|
-
};
|
|
37046
|
-
}
|
|
37047
|
-
function stripCteAlias(where, alias) {
|
|
37048
|
-
if (where === null || alias === null) return where;
|
|
37049
|
-
switch (where.type) {
|
|
37050
|
-
case "BINARY":
|
|
37051
|
-
return {
|
|
37052
|
-
type: "BINARY",
|
|
37053
|
-
op: where.op,
|
|
37054
|
-
left: stripCteAliasFromFieldValue(where.left, alias),
|
|
37055
|
-
right: where.right
|
|
37056
|
-
};
|
|
37057
|
-
case "NULL_CHECK":
|
|
37058
|
-
return {
|
|
37059
|
-
type: "NULL_CHECK",
|
|
37060
|
-
not: where.not,
|
|
37061
|
-
field: stripCteAliasFromFieldValue(where.field, alias)
|
|
37062
|
-
};
|
|
37063
|
-
case "LOGICAL":
|
|
37064
|
-
return {
|
|
37065
|
-
type: "LOGICAL",
|
|
37066
|
-
op: where.op,
|
|
37067
|
-
left: stripCteAlias(where.left, alias),
|
|
37068
|
-
right: stripCteAlias(where.right, alias)
|
|
37069
|
-
};
|
|
37070
|
-
case "NOT":
|
|
37071
|
-
return { type: "NOT", expr: stripCteAlias(where.expr, alias) };
|
|
37072
|
-
case "GROUP":
|
|
37073
|
-
return { type: "GROUP", expr: stripCteAlias(where.expr, alias) };
|
|
37074
|
-
case "EXISTS":
|
|
37075
|
-
return where;
|
|
37076
|
-
}
|
|
37077
|
-
}
|
|
37078
|
-
function stripCteAliasFromFieldValue(fv, alias) {
|
|
37079
|
-
if (fv.type === "FIELD" && fv.tableAlias === alias) {
|
|
37080
|
-
return { type: "FIELD", field: fv.field, tableAlias: null };
|
|
37081
|
-
}
|
|
37082
|
-
return fv;
|
|
37083
|
-
}
|
|
37084
37164
|
async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
|
|
37085
37165
|
if (query.type === "UNION") {
|
|
37086
37166
|
const [leftResult, rightResult] = await Promise.all([
|
|
@@ -37100,7 +37180,7 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
|
|
|
37100
37180
|
const rows = query.all ? combined : deduplicateRows(combined, leftCols);
|
|
37101
37181
|
return { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
|
|
37102
37182
|
}
|
|
37103
|
-
const hasCteRef = query.from.cteName != null || query.joins.some((j) => j.table.cteName != null);
|
|
37183
|
+
const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
|
|
37104
37184
|
if (!hasCteRef) {
|
|
37105
37185
|
return executeSelect(query, client, options, cacheContext, cteCache);
|
|
37106
37186
|
}
|
|
@@ -37115,8 +37195,13 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
37115
37195
|
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
|
|
37116
37196
|
resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
|
|
37117
37197
|
]);
|
|
37118
|
-
const typedInFieldTypes = await
|
|
37198
|
+
const [pushdownMeta, typedInFieldTypes] = await Promise.all([
|
|
37199
|
+
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
37200
|
+
loadTypedInFieldTypes(stmt, client, cacheContext)
|
|
37201
|
+
]);
|
|
37119
37202
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
37203
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
37204
|
+
validateKlikePushdownPlan(pushdownPlan);
|
|
37120
37205
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
37121
37206
|
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
37122
37207
|
scalarCachePromise.catch(() => {
|
|
@@ -37136,7 +37221,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
37136
37221
|
parallel,
|
|
37137
37222
|
true,
|
|
37138
37223
|
options.onLimitReached ?? "error",
|
|
37139
|
-
warnings
|
|
37224
|
+
warnings,
|
|
37225
|
+
pushdownPlan.mainCondition
|
|
37140
37226
|
);
|
|
37141
37227
|
tables.set(stmt.from.alias, mainRecords);
|
|
37142
37228
|
}
|
|
@@ -37145,6 +37231,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
37145
37231
|
const rows2 = cteCache.get(join.table.cteName) ?? [];
|
|
37146
37232
|
tables.set(join.table.alias, rows2.map(processRowToKintoneRecord));
|
|
37147
37233
|
} else {
|
|
37234
|
+
const pushDownCond = join.table.alias ? pushdownPlan.joinConditions.get(join.table.alias) ?? null : null;
|
|
37148
37235
|
const optimized = await tryFetchJoinRecordsBySourceKeys(
|
|
37149
37236
|
stmt,
|
|
37150
37237
|
join,
|
|
@@ -37153,7 +37240,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
37153
37240
|
maxRecords2,
|
|
37154
37241
|
parallel,
|
|
37155
37242
|
options.onLimitReached ?? "error",
|
|
37156
|
-
warnings
|
|
37243
|
+
warnings,
|
|
37244
|
+
pushDownCond
|
|
37157
37245
|
);
|
|
37158
37246
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
37159
37247
|
stmt,
|
|
@@ -37163,7 +37251,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
37163
37251
|
parallel,
|
|
37164
37252
|
false,
|
|
37165
37253
|
options.onLimitReached ?? "error",
|
|
37166
|
-
warnings
|
|
37254
|
+
warnings,
|
|
37255
|
+
pushDownCond
|
|
37167
37256
|
);
|
|
37168
37257
|
tables.set(join.table.alias, joinRecords);
|
|
37169
37258
|
}
|
|
@@ -37178,7 +37267,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
37178
37267
|
optionOrders,
|
|
37179
37268
|
sortKinds,
|
|
37180
37269
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
37181
|
-
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
37270
|
+
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
37271
|
+
appliedKlikes: pushdownPlan.appliedKlikes
|
|
37182
37272
|
});
|
|
37183
37273
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
37184
37274
|
}
|
|
@@ -38387,9 +38477,10 @@ function buildSelectPlan(stmt, label) {
|
|
|
38387
38477
|
lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
|
|
38388
38478
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
38389
38479
|
} else {
|
|
38480
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt);
|
|
38390
38481
|
const mainFields = selectToFetchAllFields(stmt, stmt.from);
|
|
38391
38482
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
38392
|
-
const mainPushDown =
|
|
38483
|
+
const mainPushDown = pushdownPlan.mainCondition;
|
|
38393
38484
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
38394
38485
|
const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
38395
38486
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
@@ -38402,7 +38493,7 @@ function buildSelectPlan(stmt, label) {
|
|
|
38402
38493
|
const joinFields = selectToFetchAllFields(stmt, join.table);
|
|
38403
38494
|
const joinAliasStr = join.table.alias ? ` AS ${join.table.alias}` : "";
|
|
38404
38495
|
const joinType = join.type === "INNER" ? "JOIN" : `${join.type} JOIN`;
|
|
38405
|
-
const joinPushDown = join.table.alias
|
|
38496
|
+
const joinPushDown = join.table.alias ? pushdownPlan.joinConditions.get(join.table.alias) ?? null : null;
|
|
38406
38497
|
const joinCandidate = join.table.alias && !join.table.subtableCode && join.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join.table.alias }) : null;
|
|
38407
38498
|
const joinQ = joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
38408
38499
|
lines.push(` ${joinType}: APP${join.table.appId}${joinAliasStr} (${join.table.appId})`);
|
|
@@ -38445,6 +38536,10 @@ function buildWithPlan(stmt) {
|
|
|
38445
38536
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
38446
38537
|
lines.push(...buildExplainPlan(stmt.query, "[main]"));
|
|
38447
38538
|
}
|
|
38539
|
+
if (canInlineSingleCte(stmt)) {
|
|
38540
|
+
lines.push("");
|
|
38541
|
+
lines.push(...buildSelectPlan(buildInlinedQuery(stmt), "[effective: inlined CTE]"));
|
|
38542
|
+
}
|
|
38448
38543
|
return lines;
|
|
38449
38544
|
}
|
|
38450
38545
|
function collectFullScanReasons(stmt) {
|
|
@@ -39122,10 +39217,11 @@ function detectSortKind(fieldType, calcFormat) {
|
|
|
39122
39217
|
}
|
|
39123
39218
|
|
|
39124
39219
|
// src/cli/nodeKintoneClient.ts
|
|
39220
|
+
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
39125
39221
|
function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
39126
39222
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
39127
39223
|
const apiBasePath = tokenResolver.guestSpaceId && tokenResolver.guestSpaceId > 0 ? `/k/guest/${tokenResolver.guestSpaceId}/v1` : "/k/v1";
|
|
39128
|
-
async function
|
|
39224
|
+
async function requestJsonResponse(path, init, appIdForToken) {
|
|
39129
39225
|
const headers = new Headers(init.headers ?? {});
|
|
39130
39226
|
if (tokenResolver.auth.type === "token") {
|
|
39131
39227
|
headers.set("X-Cybozu-API-Token", tokenResolver.auth.resolveToken(appIdForToken));
|
|
@@ -39173,7 +39269,14 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
39173
39269
|
if (tokenResolver.debug) {
|
|
39174
39270
|
tokenResolver.log?.(`[debug] response status=${res.status}`);
|
|
39175
39271
|
}
|
|
39176
|
-
|
|
39272
|
+
const warning = res.headers.get("X-Cybozu-Warning") ?? "";
|
|
39273
|
+
return {
|
|
39274
|
+
body: await res.json(),
|
|
39275
|
+
searchAborted: warning.includes(SEARCH_ABORTED_HEADER_VALUE)
|
|
39276
|
+
};
|
|
39277
|
+
}
|
|
39278
|
+
async function requestJson(path, init, appIdForToken) {
|
|
39279
|
+
return (await requestJsonResponse(path, init, appIdForToken)).body;
|
|
39177
39280
|
}
|
|
39178
39281
|
function shouldRetryWithRecordNumberOrder(path, bodyText) {
|
|
39179
39282
|
if (!path.includes("/v1/records.json?")) return false;
|
|
@@ -39207,11 +39310,12 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
39207
39310
|
}
|
|
39208
39311
|
const path = `${apiBasePath}/records.json?${qs}`;
|
|
39209
39312
|
try {
|
|
39210
|
-
|
|
39313
|
+
const response = await requestJsonResponse(
|
|
39211
39314
|
path,
|
|
39212
39315
|
{ method: "GET" },
|
|
39213
39316
|
params.app
|
|
39214
39317
|
);
|
|
39318
|
+
return response.searchAborted ? { ...response.body, searchAborted: true } : response.body;
|
|
39215
39319
|
} catch (err) {
|
|
39216
39320
|
const msg = err instanceof Error ? err.message : String(err);
|
|
39217
39321
|
if (!shouldRetryWithRecordNumberOrder(path, msg)) throw err;
|
|
@@ -39219,11 +39323,12 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
39219
39323
|
if (tokenResolver.debug) {
|
|
39220
39324
|
tokenResolver.log?.("[debug] retry with fallback query order by \u30EC\u30B3\u30FC\u30C9\u756A\u53F7 asc");
|
|
39221
39325
|
}
|
|
39222
|
-
|
|
39326
|
+
const response = await requestJsonResponse(
|
|
39223
39327
|
retryPath,
|
|
39224
39328
|
{ method: "GET" },
|
|
39225
39329
|
params.app
|
|
39226
39330
|
);
|
|
39331
|
+
return response.searchAborted ? { ...response.body, searchAborted: true } : response.body;
|
|
39227
39332
|
}
|
|
39228
39333
|
},
|
|
39229
39334
|
async postRecords(_params) {
|
|
@@ -40781,7 +40886,7 @@ Options:
|
|
|
40781
40886
|
-h, --help Show help
|
|
40782
40887
|
`);
|
|
40783
40888
|
}
|
|
40784
|
-
var SERVER_VERSION = true ? "2.
|
|
40889
|
+
var SERVER_VERSION = true ? "2.10.0" : "0.0.0-dev";
|
|
40785
40890
|
function createServer(args) {
|
|
40786
40891
|
const server = new McpServer({
|
|
40787
40892
|
name: "ksql-mcp",
|