@rex0220/kintone-sql-tools 2.8.0 → 2.9.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 +294 -253
- package/dist-mcp/ksql-mcp.js +295 -254
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -3116,6 +3116,235 @@ function isAggregateSyntheticName(name) {
|
|
|
3116
3116
|
return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
|
|
3117
3117
|
}
|
|
3118
3118
|
|
|
3119
|
+
// src/core/cteInlining.ts
|
|
3120
|
+
function canInlineSingleCte(stmt) {
|
|
3121
|
+
if (stmt.ctes.length !== 1) return false;
|
|
3122
|
+
const cteDef = stmt.ctes[0];
|
|
3123
|
+
if (cteDef.query.type !== "SELECT" || resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
|
|
3124
|
+
const finalQuery = stmt.query;
|
|
3125
|
+
if (finalQuery.type !== "SELECT") return false;
|
|
3126
|
+
if (finalQuery.from.cteName !== cteDef.name || finalQuery.joins.length > 0) return false;
|
|
3127
|
+
if (finalQuery.groupBy.length > 0 || finalQuery.distinct) return false;
|
|
3128
|
+
return !finalQuery.columns.some(
|
|
3129
|
+
(column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
|
|
3130
|
+
);
|
|
3131
|
+
}
|
|
3132
|
+
function buildInlinedQuery(stmt) {
|
|
3133
|
+
const cteBody = stmt.ctes[0].query;
|
|
3134
|
+
const final = stmt.query;
|
|
3135
|
+
const finalWhere = stripCteAlias(final.where, final.from.alias);
|
|
3136
|
+
const where = cteBody.where === null ? finalWhere : finalWhere === null ? cteBody.where : { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
|
|
3137
|
+
const columns = final.columns.every((column) => column.type === "WILDCARD") ? cteBody.columns : final.columns;
|
|
3138
|
+
return {
|
|
3139
|
+
type: "SELECT",
|
|
3140
|
+
from: cteBody.from,
|
|
3141
|
+
joins: [],
|
|
3142
|
+
columns,
|
|
3143
|
+
where,
|
|
3144
|
+
groupBy: [],
|
|
3145
|
+
having: null,
|
|
3146
|
+
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
3147
|
+
limit: final.limit ?? cteBody.limit,
|
|
3148
|
+
offset: final.offset ?? cteBody.offset,
|
|
3149
|
+
distinct: false
|
|
3150
|
+
};
|
|
3151
|
+
}
|
|
3152
|
+
function stripCteAlias(where, alias) {
|
|
3153
|
+
if (where === null || alias === null) return where;
|
|
3154
|
+
switch (where.type) {
|
|
3155
|
+
case "BINARY":
|
|
3156
|
+
return { ...where, left: stripCteAliasFromFieldValue(where.left, alias) };
|
|
3157
|
+
case "NULL_CHECK":
|
|
3158
|
+
return { ...where, field: stripCteAliasFromFieldValue(where.field, alias) };
|
|
3159
|
+
case "LOGICAL":
|
|
3160
|
+
return {
|
|
3161
|
+
...where,
|
|
3162
|
+
left: stripCteAlias(where.left, alias),
|
|
3163
|
+
right: stripCteAlias(where.right, alias)
|
|
3164
|
+
};
|
|
3165
|
+
case "NOT":
|
|
3166
|
+
case "GROUP":
|
|
3167
|
+
return { ...where, expr: stripCteAlias(where.expr, alias) };
|
|
3168
|
+
case "EXISTS":
|
|
3169
|
+
return where;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
function stripCteAliasFromFieldValue(value, alias) {
|
|
3173
|
+
if (value.type === "FIELD" && value.tableAlias === alias) {
|
|
3174
|
+
return { ...value, tableAlias: null };
|
|
3175
|
+
}
|
|
3176
|
+
return value;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
// src/core/optimization/wherePredicatePushdown.ts
|
|
3180
|
+
function extractSafePushdownLeaves(where, options = {}) {
|
|
3181
|
+
return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
|
|
3182
|
+
}
|
|
3183
|
+
function extractTypedPushdownCandidates(where, options = {}) {
|
|
3184
|
+
return extractAndLeaves(
|
|
3185
|
+
where,
|
|
3186
|
+
(expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
|
|
3187
|
+
);
|
|
3188
|
+
}
|
|
3189
|
+
function extractAndLeaves(where, accept) {
|
|
3190
|
+
switch (where.type) {
|
|
3191
|
+
case "BINARY":
|
|
3192
|
+
return accept(where) ? where : null;
|
|
3193
|
+
case "LOGICAL":
|
|
3194
|
+
if (where.op !== "AND") return null;
|
|
3195
|
+
{
|
|
3196
|
+
const left = extractAndLeaves(where.left, accept);
|
|
3197
|
+
const right = extractAndLeaves(where.right, accept);
|
|
3198
|
+
if (left && right) return { ...where, left, right };
|
|
3199
|
+
return left ?? right ?? null;
|
|
3200
|
+
}
|
|
3201
|
+
case "GROUP":
|
|
3202
|
+
return extractAndLeaves(where.expr, accept);
|
|
3203
|
+
case "NULL_CHECK":
|
|
3204
|
+
case "NOT":
|
|
3205
|
+
case "EXISTS":
|
|
3206
|
+
return null;
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
function isSafeComparison(expr, options) {
|
|
3210
|
+
if (isKlikeComparison(expr, options)) return true;
|
|
3211
|
+
if (isSafeIdComparison(expr, options)) return true;
|
|
3212
|
+
if (isNumericCandidate(expr, options)) {
|
|
3213
|
+
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
3214
|
+
}
|
|
3215
|
+
return isSelectionInComparison(expr, options);
|
|
3216
|
+
}
|
|
3217
|
+
function isKlikeComparison(expr, options) {
|
|
3218
|
+
if (options.allowKlike === false) return false;
|
|
3219
|
+
if (expr.op !== "KLIKE" && expr.op !== "NOT_KLIKE") return false;
|
|
3220
|
+
if (expr.left.type !== "FIELD" || !isTargetField(expr.left, options)) return false;
|
|
3221
|
+
return expr.right.type === "STRING" || options.allowUnresolvedKlikeVariables === true && expr.right.type === "VARIABLE";
|
|
3222
|
+
}
|
|
3223
|
+
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
3224
|
+
"DROP_DOWN",
|
|
3225
|
+
"RADIO_BUTTON",
|
|
3226
|
+
"CHECK_BOX",
|
|
3227
|
+
"MULTI_SELECT",
|
|
3228
|
+
"STATUS"
|
|
3229
|
+
]);
|
|
3230
|
+
function isSelectionInComparison(expr, options) {
|
|
3231
|
+
if (!isSelectionInCandidate(expr, options)) return false;
|
|
3232
|
+
if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
|
|
3233
|
+
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
3234
|
+
if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
|
|
3235
|
+
const validOptions = options.fieldOptions?.get(expr.left.field);
|
|
3236
|
+
if (validOptions === void 0) return false;
|
|
3237
|
+
return expr.right.values.every(
|
|
3238
|
+
(value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
3241
|
+
function isSafeIdComparison(expr, options) {
|
|
3242
|
+
if (!isTargetIdField(expr.left, options)) return false;
|
|
3243
|
+
if (expr.right.type !== "NUMBER") return false;
|
|
3244
|
+
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
3245
|
+
}
|
|
3246
|
+
function isTargetIdField(field, options) {
|
|
3247
|
+
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
3248
|
+
const targetAlias = options.tableAlias ?? null;
|
|
3249
|
+
if (field.tableAlias === targetAlias) return true;
|
|
3250
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
3251
|
+
}
|
|
3252
|
+
function isNumericCandidate(expr, options) {
|
|
3253
|
+
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
3254
|
+
if (!isTargetField(expr.left, options)) return false;
|
|
3255
|
+
if (expr.right.type !== "NUMBER") return false;
|
|
3256
|
+
if (expr.op === "=") return true;
|
|
3257
|
+
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
3258
|
+
}
|
|
3259
|
+
function isSelectionInCandidate(expr, options) {
|
|
3260
|
+
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
3261
|
+
if (!isTargetField(expr.left, options)) return false;
|
|
3262
|
+
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
3263
|
+
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
3264
|
+
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
3265
|
+
}
|
|
3266
|
+
function isTargetField(field, options) {
|
|
3267
|
+
const targetAlias = options.tableAlias ?? null;
|
|
3268
|
+
if (field.tableAlias === targetAlias) return true;
|
|
3269
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
// src/core/optimization/klikePushdownPlan.ts
|
|
3273
|
+
function buildKlikePushdownPlan(stmt, options = {}) {
|
|
3274
|
+
const joinsAreSafeForKlike = stmt.joins.every((join2) => join2.type === "INNER");
|
|
3275
|
+
const common = {
|
|
3276
|
+
allowKlike: joinsAreSafeForKlike,
|
|
3277
|
+
allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
|
|
3278
|
+
};
|
|
3279
|
+
let mainCondition = null;
|
|
3280
|
+
if (stmt.where !== null && !stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
3281
|
+
if (stmt.joins.length === 0) {
|
|
3282
|
+
mainCondition = extractSafePushdownLeaves(stmt.where, {
|
|
3283
|
+
...common,
|
|
3284
|
+
tableAlias: stmt.from.alias ?? void 0,
|
|
3285
|
+
allowUnqualifiedFields: true,
|
|
3286
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
3287
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
3288
|
+
});
|
|
3289
|
+
} else if (stmt.from.alias) {
|
|
3290
|
+
mainCondition = extractSafePushdownLeaves(stmt.where, {
|
|
3291
|
+
...common,
|
|
3292
|
+
tableAlias: stmt.from.alias,
|
|
3293
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
3294
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
3295
|
+
});
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
const joinConditions = /* @__PURE__ */ new Map();
|
|
3299
|
+
if (stmt.where !== null) {
|
|
3300
|
+
for (const join2 of stmt.joins) {
|
|
3301
|
+
if (!join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
|
|
3302
|
+
const condition = extractSafePushdownLeaves(stmt.where, {
|
|
3303
|
+
...common,
|
|
3304
|
+
tableAlias: join2.table.alias,
|
|
3305
|
+
fieldTypes: options.fieldTypesByApp?.get(join2.table.appId),
|
|
3306
|
+
fieldOptions: options.fieldOptionsByApp?.get(join2.table.appId)
|
|
3307
|
+
});
|
|
3308
|
+
if (condition !== null) joinConditions.set(join2.table.alias, condition);
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
3312
|
+
collectKlikes(mainCondition, appliedKlikes);
|
|
3313
|
+
for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
|
|
3314
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
3315
|
+
collectKlikes(stmt.where, allKlikes);
|
|
3316
|
+
return {
|
|
3317
|
+
mainCondition,
|
|
3318
|
+
joinConditions,
|
|
3319
|
+
appliedKlikes,
|
|
3320
|
+
allKlikes: [...allKlikes]
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
function unappliedKlikes(plan) {
|
|
3324
|
+
return plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr));
|
|
3325
|
+
}
|
|
3326
|
+
function collectKlikes(where, out) {
|
|
3327
|
+
if (where === null) return;
|
|
3328
|
+
if (isKlike(where)) {
|
|
3329
|
+
out.add(where);
|
|
3330
|
+
return;
|
|
3331
|
+
}
|
|
3332
|
+
switch (where.type) {
|
|
3333
|
+
case "LOGICAL":
|
|
3334
|
+
collectKlikes(where.left, out);
|
|
3335
|
+
collectKlikes(where.right, out);
|
|
3336
|
+
return;
|
|
3337
|
+
case "NOT":
|
|
3338
|
+
case "GROUP":
|
|
3339
|
+
collectKlikes(where.expr, out);
|
|
3340
|
+
return;
|
|
3341
|
+
case "BINARY":
|
|
3342
|
+
case "NULL_CHECK":
|
|
3343
|
+
case "EXISTS":
|
|
3344
|
+
return;
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3119
3348
|
// src/core/klikeValidation.ts
|
|
3120
3349
|
var KlikeValidationError = class extends Error {
|
|
3121
3350
|
constructor(message) {
|
|
@@ -3126,6 +3355,13 @@ var KlikeValidationError = class extends Error {
|
|
|
3126
3355
|
function validateKlikeStatement(stmt) {
|
|
3127
3356
|
validateStatement(stmt);
|
|
3128
3357
|
}
|
|
3358
|
+
function validateKlikePushdownPlan(plan) {
|
|
3359
|
+
if (unappliedKlikes(plan).length > 0) {
|
|
3360
|
+
throw new KlikeValidationError(
|
|
3361
|
+
"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"
|
|
3362
|
+
);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3129
3365
|
function validateStatement(stmt) {
|
|
3130
3366
|
switch (stmt.type) {
|
|
3131
3367
|
case "SELECT":
|
|
@@ -3157,7 +3393,7 @@ function validateStatement(stmt) {
|
|
|
3157
3393
|
case "REORDER":
|
|
3158
3394
|
if (containsKlike(stmt)) {
|
|
3159
3395
|
throw new KlikeValidationError(
|
|
3160
|
-
"KLIKE / NOT KLIKE \u306F
|
|
3396
|
+
"KLIKE / NOT KLIKE \u306F\u5168 DML\uFF08UPDATE / DELETE / INSERT / UPSERT / REORDER\uFF09\u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
3161
3397
|
);
|
|
3162
3398
|
}
|
|
3163
3399
|
return;
|
|
@@ -3177,9 +3413,8 @@ function validateUnion(stmt) {
|
|
|
3177
3413
|
validateSelect(stmt.right);
|
|
3178
3414
|
}
|
|
3179
3415
|
function validateWith(stmt) {
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
validateSelect(inlined);
|
|
3416
|
+
if (canInlineSingleCte(stmt)) {
|
|
3417
|
+
validateSelect(buildInlinedQuery(stmt));
|
|
3183
3418
|
return;
|
|
3184
3419
|
}
|
|
3185
3420
|
for (const cte of stmt.ctes) {
|
|
@@ -3191,10 +3426,15 @@ function validateWith(stmt) {
|
|
|
3191
3426
|
function validateSelect(stmt) {
|
|
3192
3427
|
validateOwnKlikeExpressions(stmt);
|
|
3193
3428
|
if (whereHasKlike(stmt.where)) {
|
|
3194
|
-
const
|
|
3195
|
-
if (
|
|
3429
|
+
const directKintoneSimple = resolveSelectMode(stmt) === "SIMPLE" && stmt.from.cteName === null && stmt.joins.every((join2) => join2.table.cteName === null);
|
|
3430
|
+
if (!directKintoneSimple) {
|
|
3431
|
+
const plan = buildKlikePushdownPlan(stmt, { allowUnresolvedVariables: true });
|
|
3432
|
+
if (unappliedKlikes(plan).length === 0) {
|
|
3433
|
+
validateNestedSelects(stmt);
|
|
3434
|
+
return;
|
|
3435
|
+
}
|
|
3196
3436
|
throw new KlikeValidationError(
|
|
3197
|
-
"KLIKE / NOT KLIKE \u306F
|
|
3437
|
+
"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"
|
|
3198
3438
|
);
|
|
3199
3439
|
}
|
|
3200
3440
|
}
|
|
@@ -3224,33 +3464,6 @@ function validateNestedSelects(node) {
|
|
|
3224
3464
|
if (obj.type === "SELECT") validateSelect(obj);
|
|
3225
3465
|
}, true);
|
|
3226
3466
|
}
|
|
3227
|
-
function buildEffectiveInlineSelect(stmt) {
|
|
3228
|
-
if (stmt.ctes.length !== 1) return null;
|
|
3229
|
-
const cte = stmt.ctes[0];
|
|
3230
|
-
if (cte.query.type !== "SELECT" || resolveSelectMode(cte.query) !== "SIMPLE") return null;
|
|
3231
|
-
if (stmt.query.type !== "SELECT") return null;
|
|
3232
|
-
const final = stmt.query;
|
|
3233
|
-
if (final.from.cteName !== cte.name || final.joins.length > 0) return null;
|
|
3234
|
-
if (final.groupBy.length > 0 || final.distinct) return null;
|
|
3235
|
-
if (final.columns.some(
|
|
3236
|
-
(column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
|
|
3237
|
-
)) return null;
|
|
3238
|
-
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 };
|
|
3239
|
-
const columns = final.columns.every((column) => column.type === "WILDCARD") ? cte.query.columns : final.columns;
|
|
3240
|
-
return {
|
|
3241
|
-
type: "SELECT",
|
|
3242
|
-
from: cte.query.from,
|
|
3243
|
-
joins: [],
|
|
3244
|
-
columns,
|
|
3245
|
-
where,
|
|
3246
|
-
groupBy: [],
|
|
3247
|
-
having: null,
|
|
3248
|
-
orderBy: final.orderBy.length > 0 ? final.orderBy : cte.query.orderBy,
|
|
3249
|
-
limit: final.limit ?? cte.query.limit,
|
|
3250
|
-
offset: final.offset ?? cte.query.offset,
|
|
3251
|
-
distinct: false
|
|
3252
|
-
};
|
|
3253
|
-
}
|
|
3254
3467
|
function containsKlike(node) {
|
|
3255
3468
|
let found = false;
|
|
3256
3469
|
walkObjects(node, (obj) => {
|
|
@@ -3755,25 +3968,29 @@ function resolveFieldRef(row, field) {
|
|
|
3755
3968
|
}
|
|
3756
3969
|
|
|
3757
3970
|
// src/engine/evalWhere.ts
|
|
3758
|
-
function evalWhere(expr, row, resolveFieldType) {
|
|
3971
|
+
function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
|
|
3759
3972
|
switch (expr.type) {
|
|
3760
3973
|
case "BINARY":
|
|
3761
|
-
return evalBinary(expr, row, resolveFieldType);
|
|
3974
|
+
return evalBinary(expr, row, resolveFieldType, appliedKlikes);
|
|
3762
3975
|
case "NULL_CHECK":
|
|
3763
3976
|
return evalNullCheck(expr, row);
|
|
3764
3977
|
case "LOGICAL":
|
|
3765
|
-
return evalLogical(expr, row, resolveFieldType);
|
|
3978
|
+
return evalLogical(expr, row, resolveFieldType, appliedKlikes);
|
|
3766
3979
|
case "NOT":
|
|
3767
|
-
return !evalWhere(expr.expr, row, resolveFieldType);
|
|
3980
|
+
return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
3768
3981
|
case "GROUP":
|
|
3769
|
-
return evalWhere(expr.expr, row, resolveFieldType);
|
|
3982
|
+
return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
3770
3983
|
case "EXISTS": {
|
|
3771
3984
|
const exists = expr.resolved;
|
|
3772
3985
|
return expr.not ? !exists : exists;
|
|
3773
3986
|
}
|
|
3774
3987
|
}
|
|
3775
3988
|
}
|
|
3776
|
-
function evalBinary(expr, row, resolveFieldType) {
|
|
3989
|
+
function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
|
|
3990
|
+
if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
|
|
3991
|
+
if (appliedKlikes?.has(expr)) return true;
|
|
3992
|
+
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");
|
|
3993
|
+
}
|
|
3777
3994
|
const left = resolveField(expr.left, row, resolveFieldType);
|
|
3778
3995
|
const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
|
|
3779
3996
|
return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
|
|
@@ -3855,11 +4072,11 @@ function evalNullCheck(expr, row) {
|
|
|
3855
4072
|
const val = resolveField(expr.field, row);
|
|
3856
4073
|
return expr.not ? val !== "" : val === "";
|
|
3857
4074
|
}
|
|
3858
|
-
function evalLogical(expr, row, resolveFieldType) {
|
|
4075
|
+
function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
|
|
3859
4076
|
if (expr.op === "AND") {
|
|
3860
|
-
return evalWhere(expr.left, row, resolveFieldType) && evalWhere(expr.right, row, resolveFieldType);
|
|
4077
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
3861
4078
|
}
|
|
3862
|
-
return evalWhere(expr.left, row, resolveFieldType) || evalWhere(expr.right, row, resolveFieldType);
|
|
4079
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
3863
4080
|
}
|
|
3864
4081
|
function resolveField(field, row, resolveFieldType) {
|
|
3865
4082
|
if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
|
|
@@ -3960,7 +4177,7 @@ function matchLike(value, pattern) {
|
|
|
3960
4177
|
function assertDmlWhereIsSafe(where) {
|
|
3961
4178
|
if (whereHasKlike(where)) {
|
|
3962
4179
|
throw new DmlConvertError(
|
|
3963
|
-
"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\
|
|
4180
|
+
"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"
|
|
3964
4181
|
);
|
|
3965
4182
|
}
|
|
3966
4183
|
if (!whereHasLike(where)) return;
|
|
@@ -4450,92 +4667,6 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
4450
4667
|
};
|
|
4451
4668
|
}
|
|
4452
4669
|
|
|
4453
|
-
// src/core/optimization/wherePredicatePushdown.ts
|
|
4454
|
-
function extractSafePushdownLeaves(where, options = {}) {
|
|
4455
|
-
return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
|
|
4456
|
-
}
|
|
4457
|
-
function extractTypedPushdownCandidates(where, options = {}) {
|
|
4458
|
-
return extractAndLeaves(
|
|
4459
|
-
where,
|
|
4460
|
-
(expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
|
|
4461
|
-
);
|
|
4462
|
-
}
|
|
4463
|
-
function extractAndLeaves(where, accept) {
|
|
4464
|
-
switch (where.type) {
|
|
4465
|
-
case "BINARY":
|
|
4466
|
-
return accept(where) ? where : null;
|
|
4467
|
-
case "LOGICAL":
|
|
4468
|
-
if (where.op !== "AND") return null;
|
|
4469
|
-
{
|
|
4470
|
-
const left = extractAndLeaves(where.left, accept);
|
|
4471
|
-
const right = extractAndLeaves(where.right, accept);
|
|
4472
|
-
if (left && right) return { ...where, left, right };
|
|
4473
|
-
return left ?? right ?? null;
|
|
4474
|
-
}
|
|
4475
|
-
case "GROUP":
|
|
4476
|
-
return extractAndLeaves(where.expr, accept);
|
|
4477
|
-
case "NULL_CHECK":
|
|
4478
|
-
case "NOT":
|
|
4479
|
-
case "EXISTS":
|
|
4480
|
-
return null;
|
|
4481
|
-
}
|
|
4482
|
-
}
|
|
4483
|
-
function isSafeComparison(expr, options) {
|
|
4484
|
-
if (isSafeIdComparison(expr, options)) return true;
|
|
4485
|
-
if (isNumericCandidate(expr, options)) {
|
|
4486
|
-
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
4487
|
-
}
|
|
4488
|
-
return isSelectionInComparison(expr, options);
|
|
4489
|
-
}
|
|
4490
|
-
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
4491
|
-
"DROP_DOWN",
|
|
4492
|
-
"RADIO_BUTTON",
|
|
4493
|
-
"CHECK_BOX",
|
|
4494
|
-
"MULTI_SELECT",
|
|
4495
|
-
"STATUS"
|
|
4496
|
-
]);
|
|
4497
|
-
function isSelectionInComparison(expr, options) {
|
|
4498
|
-
if (!isSelectionInCandidate(expr, options)) return false;
|
|
4499
|
-
if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
|
|
4500
|
-
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
4501
|
-
if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
|
|
4502
|
-
const validOptions = options.fieldOptions?.get(expr.left.field);
|
|
4503
|
-
if (validOptions === void 0) return false;
|
|
4504
|
-
return expr.right.values.every(
|
|
4505
|
-
(value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
|
|
4506
|
-
);
|
|
4507
|
-
}
|
|
4508
|
-
function isSafeIdComparison(expr, options) {
|
|
4509
|
-
if (!isTargetIdField(expr.left, options)) return false;
|
|
4510
|
-
if (expr.right.type !== "NUMBER") return false;
|
|
4511
|
-
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
4512
|
-
}
|
|
4513
|
-
function isTargetIdField(field, options) {
|
|
4514
|
-
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
4515
|
-
const targetAlias = options.tableAlias ?? null;
|
|
4516
|
-
if (field.tableAlias === targetAlias) return true;
|
|
4517
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
4518
|
-
}
|
|
4519
|
-
function isNumericCandidate(expr, options) {
|
|
4520
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
4521
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
4522
|
-
if (expr.right.type !== "NUMBER") return false;
|
|
4523
|
-
if (expr.op === "=") return true;
|
|
4524
|
-
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
4525
|
-
}
|
|
4526
|
-
function isSelectionInCandidate(expr, options) {
|
|
4527
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
4528
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
4529
|
-
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
4530
|
-
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
4531
|
-
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
4532
|
-
}
|
|
4533
|
-
function isTargetField(field, options) {
|
|
4534
|
-
const targetAlias = options.tableAlias ?? null;
|
|
4535
|
-
if (field.tableAlias === targetAlias) return true;
|
|
4536
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
4537
|
-
}
|
|
4538
|
-
|
|
4539
4670
|
// src/engine/process.ts
|
|
4540
4671
|
function flatten(record, alias) {
|
|
4541
4672
|
const row = {};
|
|
@@ -4600,9 +4731,9 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
4600
4731
|
}
|
|
4601
4732
|
return result;
|
|
4602
4733
|
}
|
|
4603
|
-
function applyFilter(rows, where, resolveFieldType) {
|
|
4734
|
+
function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
|
|
4604
4735
|
if (where === null) return rows;
|
|
4605
|
-
return rows.filter((row) => evalWhere(where, row, resolveFieldType));
|
|
4736
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
|
|
4606
4737
|
}
|
|
4607
4738
|
function hasAggregateColumns(columns) {
|
|
4608
4739
|
return columns.some(
|
|
@@ -5059,7 +5190,8 @@ function runFullScan(input) {
|
|
|
5059
5190
|
optionOrders,
|
|
5060
5191
|
sortKinds,
|
|
5061
5192
|
fieldTypeResolver,
|
|
5062
|
-
havingFieldTypeResolver
|
|
5193
|
+
havingFieldTypeResolver,
|
|
5194
|
+
appliedKlikes
|
|
5063
5195
|
} = input;
|
|
5064
5196
|
let rows = [];
|
|
5065
5197
|
const mainAlias = stmt.from.alias;
|
|
@@ -5071,7 +5203,7 @@ function runFullScan(input) {
|
|
|
5071
5203
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
5072
5204
|
rows = applyJoin(rows, rightRows, join2);
|
|
5073
5205
|
}
|
|
5074
|
-
rows = applyFilter(rows, stmt.where, fieldTypeResolver);
|
|
5206
|
+
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
|
|
5075
5207
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
5076
5208
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
|
|
5077
5209
|
}
|
|
@@ -5759,23 +5891,6 @@ async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
|
|
|
5759
5891
|
}
|
|
5760
5892
|
}
|
|
5761
5893
|
}
|
|
5762
|
-
function extractMainSafePushdown(stmt, fieldTypes, fieldOptions) {
|
|
5763
|
-
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
5764
|
-
if (stmt.joins.length === 0) {
|
|
5765
|
-
return extractSafePushdownLeaves(stmt.where, {
|
|
5766
|
-
tableAlias: stmt.from.alias ?? void 0,
|
|
5767
|
-
allowUnqualifiedFields: true,
|
|
5768
|
-
fieldTypes,
|
|
5769
|
-
fieldOptions
|
|
5770
|
-
});
|
|
5771
|
-
}
|
|
5772
|
-
if (!stmt.from.alias) return null;
|
|
5773
|
-
return extractSafePushdownLeaves(stmt.where, {
|
|
5774
|
-
tableAlias: stmt.from.alias,
|
|
5775
|
-
fieldTypes,
|
|
5776
|
-
fieldOptions
|
|
5777
|
-
});
|
|
5778
|
-
}
|
|
5779
5894
|
function extractMainTypedPushdownCandidate(stmt) {
|
|
5780
5895
|
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
5781
5896
|
if (stmt.joins.length === 0) {
|
|
@@ -5957,23 +6072,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
5957
6072
|
loadTypedInFieldTypes(stmt, client, cacheContext)
|
|
5958
6073
|
]);
|
|
5959
6074
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
5960
|
-
const
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
);
|
|
5965
|
-
const tableConditions = /* @__PURE__ */ new Map();
|
|
5966
|
-
if (stmt.where !== null) {
|
|
5967
|
-
for (const join2 of stmt.joins) {
|
|
5968
|
-
if (!join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
|
|
5969
|
-
const cond = extractSafePushdownLeaves(stmt.where, {
|
|
5970
|
-
tableAlias: join2.table.alias,
|
|
5971
|
-
fieldTypes: pushdownMeta.fieldTypesByApp.get(join2.table.appId),
|
|
5972
|
-
fieldOptions: pushdownMeta.fieldOptionsByApp.get(join2.table.appId)
|
|
5973
|
-
});
|
|
5974
|
-
if (cond) tableConditions.set(join2.table.alias, cond);
|
|
5975
|
-
}
|
|
5976
|
-
}
|
|
6075
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
6076
|
+
validateKlikePushdownPlan(pushdownPlan);
|
|
6077
|
+
const mainPushDown = pushdownPlan.mainCondition;
|
|
6078
|
+
const tableConditions = pushdownPlan.joinConditions;
|
|
5977
6079
|
const mainFetch = fetchTableRecordsForFullScan(
|
|
5978
6080
|
stmt,
|
|
5979
6081
|
stmt.from,
|
|
@@ -6054,7 +6156,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
6054
6156
|
optionOrders,
|
|
6055
6157
|
sortKinds,
|
|
6056
6158
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
6057
|
-
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
6159
|
+
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
6160
|
+
appliedKlikes: pushdownPlan.appliedKlikes
|
|
6058
6161
|
});
|
|
6059
6162
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
6060
6163
|
}
|
|
@@ -6103,83 +6206,6 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
|
|
|
6103
6206
|
}
|
|
6104
6207
|
return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
|
|
6105
6208
|
}
|
|
6106
|
-
function canInlineSingleCte(stmt) {
|
|
6107
|
-
if (stmt.ctes.length !== 1) return false;
|
|
6108
|
-
const cteDef = stmt.ctes[0];
|
|
6109
|
-
if (cteDef.query.type !== "SELECT") return false;
|
|
6110
|
-
if (resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
|
|
6111
|
-
const finalQuery = stmt.query;
|
|
6112
|
-
if (finalQuery.type !== "SELECT") return false;
|
|
6113
|
-
if (finalQuery.from.cteName !== cteDef.name) return false;
|
|
6114
|
-
if (finalQuery.joins.length > 0) return false;
|
|
6115
|
-
if (finalQuery.groupBy.length > 0) return false;
|
|
6116
|
-
if (finalQuery.distinct) return false;
|
|
6117
|
-
if (finalQuery.columns.some(
|
|
6118
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"
|
|
6119
|
-
)) return false;
|
|
6120
|
-
return true;
|
|
6121
|
-
}
|
|
6122
|
-
function buildInlinedQuery(stmt) {
|
|
6123
|
-
const cteBody = stmt.ctes[0].query;
|
|
6124
|
-
const final = stmt.query;
|
|
6125
|
-
const cteAlias = final.from.alias;
|
|
6126
|
-
const finalWhere = stripCteAlias(final.where, cteAlias);
|
|
6127
|
-
let mergedWhere;
|
|
6128
|
-
if (cteBody.where === null) mergedWhere = finalWhere;
|
|
6129
|
-
else if (finalWhere === null) mergedWhere = cteBody.where;
|
|
6130
|
-
else mergedWhere = { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
|
|
6131
|
-
const columns = final.columns.every((c) => c.type === "WILDCARD") ? cteBody.columns : final.columns;
|
|
6132
|
-
return {
|
|
6133
|
-
type: "SELECT",
|
|
6134
|
-
from: cteBody.from,
|
|
6135
|
-
joins: [],
|
|
6136
|
-
columns,
|
|
6137
|
-
where: mergedWhere,
|
|
6138
|
-
groupBy: [],
|
|
6139
|
-
having: null,
|
|
6140
|
-
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
6141
|
-
limit: final.limit ?? cteBody.limit,
|
|
6142
|
-
offset: final.offset ?? cteBody.offset,
|
|
6143
|
-
distinct: false
|
|
6144
|
-
};
|
|
6145
|
-
}
|
|
6146
|
-
function stripCteAlias(where, alias) {
|
|
6147
|
-
if (where === null || alias === null) return where;
|
|
6148
|
-
switch (where.type) {
|
|
6149
|
-
case "BINARY":
|
|
6150
|
-
return {
|
|
6151
|
-
type: "BINARY",
|
|
6152
|
-
op: where.op,
|
|
6153
|
-
left: stripCteAliasFromFieldValue(where.left, alias),
|
|
6154
|
-
right: where.right
|
|
6155
|
-
};
|
|
6156
|
-
case "NULL_CHECK":
|
|
6157
|
-
return {
|
|
6158
|
-
type: "NULL_CHECK",
|
|
6159
|
-
not: where.not,
|
|
6160
|
-
field: stripCteAliasFromFieldValue(where.field, alias)
|
|
6161
|
-
};
|
|
6162
|
-
case "LOGICAL":
|
|
6163
|
-
return {
|
|
6164
|
-
type: "LOGICAL",
|
|
6165
|
-
op: where.op,
|
|
6166
|
-
left: stripCteAlias(where.left, alias),
|
|
6167
|
-
right: stripCteAlias(where.right, alias)
|
|
6168
|
-
};
|
|
6169
|
-
case "NOT":
|
|
6170
|
-
return { type: "NOT", expr: stripCteAlias(where.expr, alias) };
|
|
6171
|
-
case "GROUP":
|
|
6172
|
-
return { type: "GROUP", expr: stripCteAlias(where.expr, alias) };
|
|
6173
|
-
case "EXISTS":
|
|
6174
|
-
return where;
|
|
6175
|
-
}
|
|
6176
|
-
}
|
|
6177
|
-
function stripCteAliasFromFieldValue(fv, alias) {
|
|
6178
|
-
if (fv.type === "FIELD" && fv.tableAlias === alias) {
|
|
6179
|
-
return { type: "FIELD", field: fv.field, tableAlias: null };
|
|
6180
|
-
}
|
|
6181
|
-
return fv;
|
|
6182
|
-
}
|
|
6183
6209
|
async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
|
|
6184
6210
|
if (query.type === "UNION") {
|
|
6185
6211
|
const [leftResult, rightResult] = await Promise.all([
|
|
@@ -6214,8 +6240,13 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6214
6240
|
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
|
|
6215
6241
|
resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
|
|
6216
6242
|
]);
|
|
6217
|
-
const typedInFieldTypes = await
|
|
6243
|
+
const [pushdownMeta, typedInFieldTypes] = await Promise.all([
|
|
6244
|
+
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
6245
|
+
loadTypedInFieldTypes(stmt, client, cacheContext)
|
|
6246
|
+
]);
|
|
6218
6247
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
6248
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
6249
|
+
validateKlikePushdownPlan(pushdownPlan);
|
|
6219
6250
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
6220
6251
|
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
6221
6252
|
scalarCachePromise.catch(() => {
|
|
@@ -6235,7 +6266,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6235
6266
|
parallel,
|
|
6236
6267
|
true,
|
|
6237
6268
|
options.onLimitReached ?? "error",
|
|
6238
|
-
warnings
|
|
6269
|
+
warnings,
|
|
6270
|
+
pushdownPlan.mainCondition
|
|
6239
6271
|
);
|
|
6240
6272
|
tables.set(stmt.from.alias, mainRecords);
|
|
6241
6273
|
}
|
|
@@ -6244,6 +6276,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6244
6276
|
const rows2 = cteCache.get(join2.table.cteName) ?? [];
|
|
6245
6277
|
tables.set(join2.table.alias, rows2.map(processRowToKintoneRecord));
|
|
6246
6278
|
} else {
|
|
6279
|
+
const pushDownCond = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
6247
6280
|
const optimized = await tryFetchJoinRecordsBySourceKeys(
|
|
6248
6281
|
stmt,
|
|
6249
6282
|
join2,
|
|
@@ -6252,7 +6285,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6252
6285
|
maxRecords,
|
|
6253
6286
|
parallel,
|
|
6254
6287
|
options.onLimitReached ?? "error",
|
|
6255
|
-
warnings
|
|
6288
|
+
warnings,
|
|
6289
|
+
pushDownCond
|
|
6256
6290
|
);
|
|
6257
6291
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
6258
6292
|
stmt,
|
|
@@ -6262,7 +6296,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6262
6296
|
parallel,
|
|
6263
6297
|
false,
|
|
6264
6298
|
options.onLimitReached ?? "error",
|
|
6265
|
-
warnings
|
|
6299
|
+
warnings,
|
|
6300
|
+
pushDownCond
|
|
6266
6301
|
);
|
|
6267
6302
|
tables.set(join2.table.alias, joinRecords);
|
|
6268
6303
|
}
|
|
@@ -6277,7 +6312,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
6277
6312
|
optionOrders,
|
|
6278
6313
|
sortKinds,
|
|
6279
6314
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
6280
|
-
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
6315
|
+
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
6316
|
+
appliedKlikes: pushdownPlan.appliedKlikes
|
|
6281
6317
|
});
|
|
6282
6318
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
6283
6319
|
}
|
|
@@ -7486,9 +7522,10 @@ function buildSelectPlan(stmt, label) {
|
|
|
7486
7522
|
lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
|
|
7487
7523
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
7488
7524
|
} else {
|
|
7525
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt);
|
|
7489
7526
|
const mainFields = selectToFetchAllFields(stmt, stmt.from);
|
|
7490
7527
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
7491
|
-
const mainPushDown =
|
|
7528
|
+
const mainPushDown = pushdownPlan.mainCondition;
|
|
7492
7529
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
7493
7530
|
const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
7494
7531
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
@@ -7501,7 +7538,7 @@ function buildSelectPlan(stmt, label) {
|
|
|
7501
7538
|
const joinFields = selectToFetchAllFields(stmt, join2.table);
|
|
7502
7539
|
const joinAliasStr = join2.table.alias ? ` AS ${join2.table.alias}` : "";
|
|
7503
7540
|
const joinType = join2.type === "INNER" ? "JOIN" : `${join2.type} JOIN`;
|
|
7504
|
-
const joinPushDown = join2.table.alias
|
|
7541
|
+
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
7505
7542
|
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
7506
7543
|
const joinQ = joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
7507
7544
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
@@ -7544,6 +7581,10 @@ function buildWithPlan(stmt) {
|
|
|
7544
7581
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
7545
7582
|
lines.push(...buildExplainPlan(stmt.query, "[main]"));
|
|
7546
7583
|
}
|
|
7584
|
+
if (canInlineSingleCte(stmt)) {
|
|
7585
|
+
lines.push("");
|
|
7586
|
+
lines.push(...buildSelectPlan(buildInlinedQuery(stmt), "[effective: inlined CTE]"));
|
|
7587
|
+
}
|
|
7547
7588
|
return lines;
|
|
7548
7589
|
}
|
|
7549
7590
|
function collectFullScanReasons(stmt) {
|