@rex0220/kintone-sql-tools 3.60.0 → 3.61.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 +580 -358
- package/dist-engine/index.cjs +13 -13
- package/dist-engine/index.mjs +13 -13
- package/dist-engine/ksql-engine.umd.js +13 -13
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +54 -25
- package/dist-engine/meta/esm.json +54 -25
- package/dist-engine/meta/umd.json +54 -25
- package/dist-mcp/ksql-mcp.js +513 -288
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -12042,251 +12042,27 @@ function isOuterJoinNonPreservedTable(statement, table, isMainTable) {
|
|
|
12042
12042
|
return false;
|
|
12043
12043
|
}
|
|
12044
12044
|
|
|
12045
|
-
// src/core/
|
|
12046
|
-
function
|
|
12047
|
-
const
|
|
12048
|
-
if (
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
}
|
|
12058
|
-
function whereNeedsFieldMetadata(where) {
|
|
12059
|
-
if (where === null) return false;
|
|
12060
|
-
switch (where.type) {
|
|
12061
|
-
case "BINARY":
|
|
12062
|
-
return valueNeedsFieldMetadata(where.left);
|
|
12063
|
-
case "NULL_CHECK":
|
|
12064
|
-
return valueNeedsFieldMetadata(where.field);
|
|
12065
|
-
case "LOGICAL":
|
|
12066
|
-
return whereNeedsFieldMetadata(where.left) || whereNeedsFieldMetadata(where.right);
|
|
12067
|
-
case "NOT":
|
|
12068
|
-
case "GROUP":
|
|
12069
|
-
return whereNeedsFieldMetadata(where.expr);
|
|
12070
|
-
case "EXISTS":
|
|
12071
|
-
case "BOOLEAN":
|
|
12072
|
-
return false;
|
|
12073
|
-
}
|
|
12074
|
-
}
|
|
12075
|
-
function valueNeedsFieldMetadata(value) {
|
|
12076
|
-
if (Array.isArray(value)) return value.some(valueNeedsFieldMetadata);
|
|
12077
|
-
if (value === null || typeof value !== "object") return false;
|
|
12078
|
-
const item = value;
|
|
12079
|
-
if (item["type"] === "FIELD") return item["field"] !== "$id";
|
|
12080
|
-
if (item["type"] === "SELECT") return false;
|
|
12081
|
-
return Object.values(item).some(valueNeedsFieldMetadata);
|
|
12082
|
-
}
|
|
12083
|
-
function selectNeedsOwnMetadata(statement) {
|
|
12084
|
-
return whereNeedsFieldMetadata(statement.where) || statement.groupBy.length > 0 || normalizeGroupingSpec(statement).type === "GROUPING_SETS" || statement.orderBy.length > 0 || statement.columns.some(
|
|
12085
|
-
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
12086
|
-
);
|
|
12087
|
-
}
|
|
12088
|
-
function explainNeedsAppMetadata(statement) {
|
|
12089
|
-
const seen = /* @__PURE__ */ new Set();
|
|
12090
|
-
const visit = (node) => {
|
|
12091
|
-
if (node === null || typeof node !== "object") return false;
|
|
12092
|
-
if (seen.has(node)) return false;
|
|
12093
|
-
seen.add(node);
|
|
12094
|
-
if (Array.isArray(node)) return node.some(visit);
|
|
12095
|
-
const item = node;
|
|
12096
|
-
if (item["type"] === "VALIDATE") return true;
|
|
12097
|
-
if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
|
|
12098
|
-
return true;
|
|
12099
|
-
}
|
|
12100
|
-
if ((item["type"] === "UPDATE" || item["type"] === "DELETE") && whereNeedsFieldMetadata(node.where)) {
|
|
12101
|
-
return true;
|
|
12102
|
-
}
|
|
12103
|
-
if (item["type"] === "UPDATE" && Array.isArray(item["applyBlocks"]) && item["applyBlocks"].length > 0) return true;
|
|
12104
|
-
return Object.values(item).some(visit);
|
|
12105
|
-
};
|
|
12106
|
-
return visit(statement);
|
|
12107
|
-
}
|
|
12108
|
-
|
|
12109
|
-
// src/api/fetchAll.ts
|
|
12110
|
-
async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
12111
|
-
const pageSize = options.pageSize ?? PAGE_SIZE_DEFAULT;
|
|
12112
|
-
const parallel = Math.max(1, options.parallel ?? PARALLEL_DEFAULT);
|
|
12113
|
-
const maxRecords = options.maxRecords ?? MAX_RECORDS_DEFAULT;
|
|
12114
|
-
const onLimit = options.onLimit ?? "error";
|
|
12115
|
-
const stopAfter = options.stopAfter;
|
|
12116
|
-
if (stopAfter !== void 0 && (!Number.isSafeInteger(stopAfter) || stopAfter <= 0 || stopAfter > maxRecords)) {
|
|
12117
|
-
throw new RangeError("stopAfter must be a positive safe integer <= maxRecords");
|
|
12118
|
-
}
|
|
12119
|
-
const fetchCap = stopAfter ?? maxRecords;
|
|
12120
|
-
const fetchFields = fields.length > 0 && !fields.includes("$id") ? [...fields, "$id"] : fields;
|
|
12121
|
-
const allRecords = [];
|
|
12122
|
-
let notified = false;
|
|
12123
|
-
const limitMessage = `\u53D6\u5F97\u4EF6\u6570\u304C\u4E0A\u9650\uFF08${maxRecords} \u4EF6\uFF09\u3092\u8D85\u3048\u307E\u3057\u305F\u3002WHERE \u53E5\u3067\u7D5E\u308A\u8FBC\u3080\u304B\u3001maxRecords \u3092\u5F15\u304D\u4E0A\u3052\u3066\u304F\u3060\u3055\u3044\u3002`;
|
|
12124
|
-
let cursorId = 0;
|
|
12125
|
-
let windowOffset = 0;
|
|
12126
|
-
const cursorQuery0 = buildCursorQuery(query, cursorId);
|
|
12127
|
-
const first = await fetchPage(fetcher, app, cursorQuery0, fetchFields, pageSize, windowOffset);
|
|
12128
|
-
notifySearchAborted(first, options);
|
|
12129
|
-
allRecords.push(...first.records);
|
|
12130
|
-
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12131
|
-
return allRecords.slice(0, stopAfter);
|
|
12132
|
-
}
|
|
12133
|
-
if (allRecords.length > maxRecords) {
|
|
12134
|
-
if (onLimit === "truncate") {
|
|
12135
|
-
if (!notified && options.onTruncate) {
|
|
12136
|
-
options.onTruncate(maxRecords);
|
|
12137
|
-
notified = true;
|
|
12138
|
-
}
|
|
12139
|
-
return allRecords.slice(0, maxRecords);
|
|
12140
|
-
}
|
|
12141
|
-
throw new FetchAllLimitError(limitMessage);
|
|
12142
|
-
}
|
|
12143
|
-
if (first.records.length < pageSize) return allRecords;
|
|
12144
|
-
windowOffset += pageSize;
|
|
12145
|
-
if (windowOffset >= KINTONE_MAX_OFFSET) {
|
|
12146
|
-
cursorId = getLastId(first.records);
|
|
12147
|
-
windowOffset = 0;
|
|
12148
|
-
}
|
|
12149
|
-
while (true) {
|
|
12150
|
-
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12151
|
-
return allRecords.slice(0, stopAfter);
|
|
12152
|
-
}
|
|
12153
|
-
if (allRecords.length >= maxRecords) {
|
|
12154
|
-
if (onLimit === "truncate") {
|
|
12155
|
-
if (!notified && options.onTruncate) {
|
|
12156
|
-
options.onTruncate(maxRecords);
|
|
12157
|
-
notified = true;
|
|
12158
|
-
}
|
|
12159
|
-
return allRecords.slice(0, maxRecords);
|
|
12160
|
-
}
|
|
12161
|
-
throw new FetchAllLimitError(limitMessage);
|
|
12162
|
-
}
|
|
12163
|
-
const remaining = fetchCap - allRecords.length;
|
|
12164
|
-
const maxPagesByLimit = Math.ceil(remaining / pageSize);
|
|
12165
|
-
const batchParallel = Math.max(1, Math.min(parallel, maxPagesByLimit));
|
|
12166
|
-
const batchOffsets = [];
|
|
12167
|
-
for (let i = 0; i < batchParallel; i++) {
|
|
12168
|
-
const off = windowOffset + i * pageSize;
|
|
12169
|
-
if (off >= KINTONE_MAX_OFFSET) break;
|
|
12170
|
-
batchOffsets.push(off);
|
|
12171
|
-
}
|
|
12172
|
-
const cq = buildCursorQuery(query, cursorId);
|
|
12173
|
-
const responses = await Promise.all(
|
|
12174
|
-
batchOffsets.map(
|
|
12175
|
-
(offset) => fetchPage(fetcher, app, cq, fetchFields, pageSize, offset)
|
|
12176
|
-
)
|
|
12177
|
-
);
|
|
12178
|
-
for (const response of responses) notifySearchAborted(response, options);
|
|
12179
|
-
let done = false;
|
|
12180
|
-
for (const res of responses) {
|
|
12181
|
-
allRecords.push(...res.records);
|
|
12182
|
-
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12183
|
-
return allRecords.slice(0, stopAfter);
|
|
12184
|
-
}
|
|
12185
|
-
if (allRecords.length > maxRecords) {
|
|
12186
|
-
if (onLimit === "truncate") {
|
|
12187
|
-
if (!notified && options.onTruncate) {
|
|
12188
|
-
options.onTruncate(maxRecords);
|
|
12189
|
-
notified = true;
|
|
12190
|
-
}
|
|
12191
|
-
return allRecords.slice(0, maxRecords);
|
|
12192
|
-
}
|
|
12193
|
-
throw new FetchAllLimitError(limitMessage);
|
|
12194
|
-
}
|
|
12195
|
-
if (res.records.length < pageSize) {
|
|
12196
|
-
done = true;
|
|
12197
|
-
break;
|
|
12198
|
-
}
|
|
12199
|
-
}
|
|
12200
|
-
if (done) break;
|
|
12201
|
-
windowOffset += pageSize * batchOffsets.length;
|
|
12202
|
-
if (windowOffset >= KINTONE_MAX_OFFSET) {
|
|
12203
|
-
const lastRes = responses[responses.length - 1];
|
|
12204
|
-
cursorId = getLastId(lastRes.records);
|
|
12205
|
-
windowOffset = 0;
|
|
12206
|
-
}
|
|
12207
|
-
}
|
|
12208
|
-
return allRecords;
|
|
12209
|
-
}
|
|
12210
|
-
function notifySearchAborted(response, options) {
|
|
12211
|
-
if (response.searchAborted) options.onSearchAborted?.();
|
|
12212
|
-
}
|
|
12213
|
-
function extractIds(records) {
|
|
12214
|
-
return records.map((r) => {
|
|
12215
|
-
const raw = r["$id"]?.value;
|
|
12216
|
-
if (raw === void 0) {
|
|
12217
|
-
throw new Error(
|
|
12218
|
-
'\u30EC\u30B3\u30FC\u30C9\u306B $id \u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093\u3002fields \u306B "$id" \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002'
|
|
12219
|
-
);
|
|
12220
|
-
}
|
|
12221
|
-
const id = Number(raw);
|
|
12222
|
-
if (!Number.isFinite(id)) {
|
|
12223
|
-
throw new Error(`$id \u306E\u5024\u304C\u6570\u5024\u3067\u306F\u3042\u308A\u307E\u305B\u3093: ${raw}`);
|
|
12224
|
-
}
|
|
12225
|
-
return id;
|
|
12226
|
-
});
|
|
12227
|
-
}
|
|
12228
|
-
var PAGE_SIZE_DEFAULT = 500;
|
|
12229
|
-
var PARALLEL_DEFAULT = 1;
|
|
12230
|
-
var MAX_RECORDS_DEFAULT = 1e4;
|
|
12231
|
-
var KINTONE_MAX_OFFSET = 1e4;
|
|
12232
|
-
async function fetchPage(fetcher, app, query, fields, pageSize, offset) {
|
|
12233
|
-
const pageQuery = buildPageQuery(query, pageSize, offset);
|
|
12234
|
-
return fetcher({ app, query: pageQuery, fields });
|
|
12235
|
-
}
|
|
12236
|
-
function buildCursorQuery(baseQuery, cursorId) {
|
|
12237
|
-
const base = baseQuery.trimEnd();
|
|
12238
|
-
if (cursorId <= 0) {
|
|
12239
|
-
return base ? `${base} order by $id asc` : "order by $id asc";
|
|
12240
|
-
}
|
|
12241
|
-
const cursor = `$id > ${cursorId} order by $id asc`;
|
|
12242
|
-
return base ? `(${base}) and ${cursor}` : cursor;
|
|
12243
|
-
}
|
|
12244
|
-
function buildPageQuery(query, pageSize, offset) {
|
|
12245
|
-
const base = query.trimEnd();
|
|
12246
|
-
const suffix = `limit ${pageSize} offset ${offset}`;
|
|
12247
|
-
return base ? `${base} ${suffix}` : suffix;
|
|
12248
|
-
}
|
|
12249
|
-
function getLastId(records) {
|
|
12250
|
-
const last = records[records.length - 1];
|
|
12251
|
-
const raw = last?.["$id"]?.value;
|
|
12252
|
-
if (raw === void 0) return 0;
|
|
12253
|
-
const id = Number(raw);
|
|
12254
|
-
return Number.isFinite(id) ? id : 0;
|
|
12045
|
+
// src/core/optimization/joinDateTimeLiteralPolicy.ts
|
|
12046
|
+
function isCanonicalJoinDate(value) {
|
|
12047
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
12048
|
+
if (!match) return false;
|
|
12049
|
+
const year = Number(match[1]);
|
|
12050
|
+
const month = Number(match[2]);
|
|
12051
|
+
const day = Number(match[3]);
|
|
12052
|
+
if (year < 1 || year > 9999) return false;
|
|
12053
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
12054
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
12055
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
12056
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
12255
12057
|
}
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
this.completeInputWrapped = completeInputWrapped;
|
|
12260
|
-
this.name = "FetchAllLimitError";
|
|
12261
|
-
}
|
|
12262
|
-
};
|
|
12263
|
-
|
|
12264
|
-
// src/core/optimization/sharedPlanner.ts
|
|
12265
|
-
async function fetchRecordsForSharedPlan(getRecords, app, query, fields, options) {
|
|
12266
|
-
const records = await fetchAll(getRecords, app, query, fields, {
|
|
12267
|
-
maxRecords: options.maxRecords,
|
|
12268
|
-
parallel: options.parallel,
|
|
12269
|
-
onLimit: options.onLimit ?? "error",
|
|
12270
|
-
onTruncate: options.onTruncate,
|
|
12271
|
-
onSearchAborted: options.onSearchAborted
|
|
12272
|
-
});
|
|
12273
|
-
return {
|
|
12274
|
-
records,
|
|
12275
|
-
metrics: { fetchedRows: records.length }
|
|
12276
|
-
};
|
|
12058
|
+
function isCanonicalJoinTime(value) {
|
|
12059
|
+
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
12060
|
+
return match !== null && Number(match[1]) <= 23 && Number(match[2]) <= 59;
|
|
12277
12061
|
}
|
|
12278
|
-
|
|
12279
|
-
const
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
query,
|
|
12283
|
-
["$id"],
|
|
12284
|
-
options
|
|
12285
|
-
);
|
|
12286
|
-
return {
|
|
12287
|
-
ids: extractIds(records),
|
|
12288
|
-
metrics
|
|
12289
|
-
};
|
|
12062
|
+
function isCanonicalJoinDateTime(value) {
|
|
12063
|
+
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.exec(value);
|
|
12064
|
+
if (!match || !isCanonicalJoinDate(match[1])) return false;
|
|
12065
|
+
return Number(match[2]) <= 23 && Number(match[3]) <= 59 && Number(match[4]) <= 59;
|
|
12290
12066
|
}
|
|
12291
12067
|
|
|
12292
12068
|
// src/core/optimization/whereCapability.ts
|
|
@@ -12701,117 +12477,432 @@ function combineLogical(op, left, right) {
|
|
|
12701
12477
|
if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
|
|
12702
12478
|
return requireExactFunctionPushdown({ capability: "UNSUPPORTED", reasons });
|
|
12703
12479
|
}
|
|
12704
|
-
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
12705
|
-
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
12480
|
+
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
12481
|
+
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
12482
|
+
}
|
|
12483
|
+
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
12484
|
+
return requireExactFunctionPushdown({
|
|
12485
|
+
capability: "SUPERSET_PREFILTER",
|
|
12486
|
+
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
12487
|
+
});
|
|
12488
|
+
}
|
|
12489
|
+
return requireExactFunctionPushdown({ capability: "LOCAL_ONLY", reasons });
|
|
12490
|
+
}
|
|
12491
|
+
function localExpression() {
|
|
12492
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12493
|
+
}
|
|
12494
|
+
function unsupported2(code, field, fieldType, operator) {
|
|
12495
|
+
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
12496
|
+
}
|
|
12497
|
+
function legacyKintoneFunctionUnsupported(code, functionName, field, fieldType, operator) {
|
|
12498
|
+
return requireExactFunctionPushdown({
|
|
12499
|
+
capability: "UNSUPPORTED",
|
|
12500
|
+
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12501
|
+
});
|
|
12502
|
+
}
|
|
12503
|
+
function relativeDateUnsupported(code, functionName, field, fieldType, operator) {
|
|
12504
|
+
return requireExactRelativeDatePushdown({
|
|
12505
|
+
capability: "UNSUPPORTED",
|
|
12506
|
+
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12507
|
+
});
|
|
12508
|
+
}
|
|
12509
|
+
function hasRelativeDateReason(reasons) {
|
|
12510
|
+
return reasons.some(
|
|
12511
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
12512
|
+
);
|
|
12513
|
+
}
|
|
12514
|
+
function hasLegacyKintoneFunctionReason(reasons) {
|
|
12515
|
+
return reasons.some(
|
|
12516
|
+
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
12517
|
+
);
|
|
12518
|
+
}
|
|
12519
|
+
function requireExactRelativeDatePushdown(result) {
|
|
12520
|
+
if (result.capability === "EXACT_PUSHDOWN" || !hasRelativeDateReason(result.reasons) || result.reasons.some((reason) => reason.code === "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN")) {
|
|
12521
|
+
return result;
|
|
12522
|
+
}
|
|
12523
|
+
const relative = result.reasons.find(
|
|
12524
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
12525
|
+
);
|
|
12526
|
+
return {
|
|
12527
|
+
capability: result.capability,
|
|
12528
|
+
reasons: [
|
|
12529
|
+
...result.reasons,
|
|
12530
|
+
{
|
|
12531
|
+
code: "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN",
|
|
12532
|
+
functionName: relative.functionName,
|
|
12533
|
+
field: relative.field,
|
|
12534
|
+
fieldType: relative.fieldType,
|
|
12535
|
+
operator: relative.operator
|
|
12536
|
+
}
|
|
12537
|
+
]
|
|
12538
|
+
};
|
|
12539
|
+
}
|
|
12540
|
+
function requireExactLegacyKintoneFunctionPushdown(result) {
|
|
12541
|
+
if (result.capability === "EXACT_PUSHDOWN" || !hasLegacyKintoneFunctionReason(result.reasons) || result.reasons.some(
|
|
12542
|
+
(reason) => reason.code === "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN"
|
|
12543
|
+
)) {
|
|
12544
|
+
return result;
|
|
12545
|
+
}
|
|
12546
|
+
const legacy = result.reasons.find(
|
|
12547
|
+
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
12548
|
+
);
|
|
12549
|
+
return {
|
|
12550
|
+
capability: result.capability,
|
|
12551
|
+
reasons: [
|
|
12552
|
+
...result.reasons,
|
|
12553
|
+
{
|
|
12554
|
+
code: "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN",
|
|
12555
|
+
functionName: legacy.functionName,
|
|
12556
|
+
field: legacy.field,
|
|
12557
|
+
fieldType: legacy.fieldType,
|
|
12558
|
+
operator: legacy.operator
|
|
12559
|
+
}
|
|
12560
|
+
]
|
|
12561
|
+
};
|
|
12562
|
+
}
|
|
12563
|
+
function requireExactFunctionPushdown(result) {
|
|
12564
|
+
return requireExactLegacyKintoneFunctionPushdown(
|
|
12565
|
+
requireExactRelativeDatePushdown(result)
|
|
12566
|
+
);
|
|
12567
|
+
}
|
|
12568
|
+
|
|
12569
|
+
// src/core/optimization/joinKeyPrefilter.ts
|
|
12570
|
+
var JOIN_KEY_IN_CHUNK_SIZE = 50;
|
|
12571
|
+
function buildJoinKeyPrefilterQueries(plan, field, quoteValue) {
|
|
12572
|
+
if (plan.kind === "RANGE") {
|
|
12573
|
+
return [`${field} >= ${quoteValue(plan.min)} and ${field} <= ${quoteValue(plan.max)}`];
|
|
12574
|
+
}
|
|
12575
|
+
if (plan.kind !== "IN") return [];
|
|
12576
|
+
const queries = [];
|
|
12577
|
+
for (let offset = 0; offset < plan.values.length; offset += JOIN_KEY_IN_CHUNK_SIZE) {
|
|
12578
|
+
const chunk3 = plan.values.slice(offset, offset + JOIN_KEY_IN_CHUNK_SIZE);
|
|
12579
|
+
queries.push(`${field} in (${chunk3.map(quoteValue).join(",")})`);
|
|
12580
|
+
}
|
|
12581
|
+
return queries;
|
|
12582
|
+
}
|
|
12583
|
+
var DATETIME_TYPES = /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"]);
|
|
12584
|
+
var JOIN_KEY_EMPTY_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
12585
|
+
"SINGLE_LINE_TEXT",
|
|
12586
|
+
"LINK",
|
|
12587
|
+
"NUMBER",
|
|
12588
|
+
"CALC",
|
|
12589
|
+
"DROP_DOWN",
|
|
12590
|
+
"RADIO_BUTTON",
|
|
12591
|
+
"CHECK_BOX",
|
|
12592
|
+
"MULTI_SELECT",
|
|
12593
|
+
"STATUS"
|
|
12594
|
+
]);
|
|
12595
|
+
function canonicalFor(fieldType, value) {
|
|
12596
|
+
if (fieldType === "DATE") return isCanonicalJoinDate(value);
|
|
12597
|
+
if (fieldType === "TIME") return isCanonicalJoinTime(value);
|
|
12598
|
+
if (DATETIME_TYPES.has(fieldType)) return isCanonicalJoinDateTime(value);
|
|
12599
|
+
return false;
|
|
12600
|
+
}
|
|
12601
|
+
function semanticsMatch(fieldType, semantics) {
|
|
12602
|
+
if (!semantics || semantics.compareMode !== "string") return false;
|
|
12603
|
+
if (fieldType === "DATE") return semantics.fieldType === "DATE";
|
|
12604
|
+
if (fieldType === "TIME") return semantics.fieldType === "TIME";
|
|
12605
|
+
if (DATETIME_TYPES.has(fieldType)) return DATETIME_TYPES.has(semantics.fieldType);
|
|
12606
|
+
return false;
|
|
12607
|
+
}
|
|
12608
|
+
function planJoinKeyPrefilter(input) {
|
|
12609
|
+
if (input.sourceRowCount === 0) return { kind: "EMPTY_SOURCE" };
|
|
12610
|
+
if (!input.fieldType) {
|
|
12611
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_FIELD_TYPE_UNRESOLVED" };
|
|
12612
|
+
}
|
|
12613
|
+
const operators = nativeWhereOperatorsForType(input.fieldType);
|
|
12614
|
+
if (operators.has("in")) {
|
|
12615
|
+
if (input.values === void 0) {
|
|
12616
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_VALUES_RUNTIME" };
|
|
12617
|
+
}
|
|
12618
|
+
if (input.hasEmptyValue && !JOIN_KEY_EMPTY_IN_FIELD_TYPES.has(input.fieldType)) {
|
|
12619
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_EMPTY_VALUE" };
|
|
12620
|
+
}
|
|
12621
|
+
const values2 = [...new Set(input.values)];
|
|
12622
|
+
if (values2.length === 0) return { kind: "EMPTY_SOURCE" };
|
|
12623
|
+
if (values2.length > input.maxInKeys) {
|
|
12624
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_LIMIT_EXCEEDED" };
|
|
12625
|
+
}
|
|
12626
|
+
return { kind: "IN", values: values2, relation: "exact" };
|
|
12627
|
+
}
|
|
12628
|
+
if (!operators.has(">=") || !operators.has("<=")) {
|
|
12629
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_OPERATOR_UNAVAILABLE" };
|
|
12630
|
+
}
|
|
12631
|
+
if (input.values === void 0) {
|
|
12632
|
+
return { kind: "RANGE_CANDIDATE", relation: "superset", reason: "JOIN_KEY_VALUES_RUNTIME" };
|
|
12633
|
+
}
|
|
12634
|
+
if (!semanticsMatch(input.fieldType, input.sourceSemantics)) {
|
|
12635
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_SEMANTICS_UNRESOLVED" };
|
|
12636
|
+
}
|
|
12637
|
+
const sourceSemantics = input.sourceSemantics;
|
|
12638
|
+
if (input.hasEmptyValue) {
|
|
12639
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_EMPTY_VALUE" };
|
|
12640
|
+
}
|
|
12641
|
+
const values = [...new Set(input.values)];
|
|
12642
|
+
if (values.some((value) => !canonicalFor(input.fieldType, value))) {
|
|
12643
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_NON_CANONICAL_VALUE" };
|
|
12644
|
+
}
|
|
12645
|
+
if (values.length === 0) {
|
|
12646
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_EMPTY_VALUE" };
|
|
12647
|
+
}
|
|
12648
|
+
let min = values[0];
|
|
12649
|
+
let max = values[0];
|
|
12650
|
+
for (let i = 1; i < values.length; i += 1) {
|
|
12651
|
+
const value = values[i];
|
|
12652
|
+
if (compareCanonicalValues(value, min, sourceSemantics) < 0) min = value;
|
|
12653
|
+
if (compareCanonicalValues(value, max, sourceSemantics) > 0) max = value;
|
|
12654
|
+
}
|
|
12655
|
+
return { kind: "RANGE", min, max, relation: "superset" };
|
|
12656
|
+
}
|
|
12657
|
+
|
|
12658
|
+
// src/core/explainMetadata.ts
|
|
12659
|
+
function buildGroupingExplainMetadata(statement, canonicalItemCount) {
|
|
12660
|
+
const grouping = normalizeGroupingSpec(statement);
|
|
12661
|
+
if (grouping.type !== "GROUPING_SETS") return null;
|
|
12662
|
+
return {
|
|
12663
|
+
source: grouping.source,
|
|
12664
|
+
expandedSetCount: grouping.sets.length,
|
|
12665
|
+
groupingItemCount: canonicalItemCount ?? grouping.allItems.length,
|
|
12666
|
+
setLimit: B65_MAX_GROUPING_SETS,
|
|
12667
|
+
itemLimit: B65_MAX_GROUPING_ITEMS,
|
|
12668
|
+
outputRowLimit: B65_MAX_GENERATED_ROWS
|
|
12669
|
+
};
|
|
12670
|
+
}
|
|
12671
|
+
function whereNeedsFieldMetadata(where) {
|
|
12672
|
+
if (where === null) return false;
|
|
12673
|
+
switch (where.type) {
|
|
12674
|
+
case "BINARY":
|
|
12675
|
+
return valueNeedsFieldMetadata(where.left);
|
|
12676
|
+
case "NULL_CHECK":
|
|
12677
|
+
return valueNeedsFieldMetadata(where.field);
|
|
12678
|
+
case "LOGICAL":
|
|
12679
|
+
return whereNeedsFieldMetadata(where.left) || whereNeedsFieldMetadata(where.right);
|
|
12680
|
+
case "NOT":
|
|
12681
|
+
case "GROUP":
|
|
12682
|
+
return whereNeedsFieldMetadata(where.expr);
|
|
12683
|
+
case "EXISTS":
|
|
12684
|
+
case "BOOLEAN":
|
|
12685
|
+
return false;
|
|
12686
|
+
}
|
|
12687
|
+
}
|
|
12688
|
+
function valueNeedsFieldMetadata(value) {
|
|
12689
|
+
if (Array.isArray(value)) return value.some(valueNeedsFieldMetadata);
|
|
12690
|
+
if (value === null || typeof value !== "object") return false;
|
|
12691
|
+
const item = value;
|
|
12692
|
+
if (item["type"] === "FIELD") return item["field"] !== "$id";
|
|
12693
|
+
if (item["type"] === "SELECT") return false;
|
|
12694
|
+
return Object.values(item).some(valueNeedsFieldMetadata);
|
|
12695
|
+
}
|
|
12696
|
+
function selectNeedsOwnMetadata(statement) {
|
|
12697
|
+
return whereNeedsFieldMetadata(statement.where) || statement.groupBy.length > 0 || normalizeGroupingSpec(statement).type === "GROUPING_SETS" || statement.orderBy.length > 0 || statement.joins.some(
|
|
12698
|
+
(join2) => join2.table.appId > 0 && join2.table.cteName === null && (join2.on.left.field !== "$id" || join2.on.right.field !== "$id")
|
|
12699
|
+
) || statement.columns.some(
|
|
12700
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
12701
|
+
);
|
|
12702
|
+
}
|
|
12703
|
+
function explainNeedsAppMetadata(statement) {
|
|
12704
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12705
|
+
const visit = (node) => {
|
|
12706
|
+
if (node === null || typeof node !== "object") return false;
|
|
12707
|
+
if (seen.has(node)) return false;
|
|
12708
|
+
seen.add(node);
|
|
12709
|
+
if (Array.isArray(node)) return node.some(visit);
|
|
12710
|
+
const item = node;
|
|
12711
|
+
if (item["type"] === "VALIDATE") return true;
|
|
12712
|
+
if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
|
|
12713
|
+
return true;
|
|
12714
|
+
}
|
|
12715
|
+
if ((item["type"] === "UPDATE" || item["type"] === "DELETE") && whereNeedsFieldMetadata(node.where)) {
|
|
12716
|
+
return true;
|
|
12717
|
+
}
|
|
12718
|
+
if (item["type"] === "UPDATE" && Array.isArray(item["applyBlocks"]) && item["applyBlocks"].length > 0) return true;
|
|
12719
|
+
return Object.values(item).some(visit);
|
|
12720
|
+
};
|
|
12721
|
+
return visit(statement);
|
|
12722
|
+
}
|
|
12723
|
+
|
|
12724
|
+
// src/api/fetchAll.ts
|
|
12725
|
+
async function fetchAll(fetcher, app, query, fields, options = {}) {
|
|
12726
|
+
const pageSize = options.pageSize ?? PAGE_SIZE_DEFAULT;
|
|
12727
|
+
const parallel = Math.max(1, options.parallel ?? PARALLEL_DEFAULT);
|
|
12728
|
+
const maxRecords = options.maxRecords ?? MAX_RECORDS_DEFAULT;
|
|
12729
|
+
const onLimit = options.onLimit ?? "error";
|
|
12730
|
+
const stopAfter = options.stopAfter;
|
|
12731
|
+
if (stopAfter !== void 0 && (!Number.isSafeInteger(stopAfter) || stopAfter <= 0 || stopAfter > maxRecords)) {
|
|
12732
|
+
throw new RangeError("stopAfter must be a positive safe integer <= maxRecords");
|
|
12733
|
+
}
|
|
12734
|
+
const fetchCap = stopAfter ?? maxRecords;
|
|
12735
|
+
const fetchFields = fields.length > 0 && !fields.includes("$id") ? [...fields, "$id"] : fields;
|
|
12736
|
+
const allRecords = [];
|
|
12737
|
+
let notified = false;
|
|
12738
|
+
const limitMessage = `\u53D6\u5F97\u4EF6\u6570\u304C\u4E0A\u9650\uFF08${maxRecords} \u4EF6\uFF09\u3092\u8D85\u3048\u307E\u3057\u305F\u3002WHERE \u53E5\u3067\u7D5E\u308A\u8FBC\u3080\u304B\u3001maxRecords \u3092\u5F15\u304D\u4E0A\u3052\u3066\u304F\u3060\u3055\u3044\u3002`;
|
|
12739
|
+
let cursorId = 0;
|
|
12740
|
+
let windowOffset = 0;
|
|
12741
|
+
const cursorQuery0 = buildCursorQuery(query, cursorId);
|
|
12742
|
+
const first = await fetchPage(fetcher, app, cursorQuery0, fetchFields, pageSize, windowOffset);
|
|
12743
|
+
notifySearchAborted(first, options);
|
|
12744
|
+
allRecords.push(...first.records);
|
|
12745
|
+
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12746
|
+
return allRecords.slice(0, stopAfter);
|
|
12747
|
+
}
|
|
12748
|
+
if (allRecords.length > maxRecords) {
|
|
12749
|
+
if (onLimit === "truncate") {
|
|
12750
|
+
if (!notified && options.onTruncate) {
|
|
12751
|
+
options.onTruncate(maxRecords);
|
|
12752
|
+
notified = true;
|
|
12753
|
+
}
|
|
12754
|
+
return allRecords.slice(0, maxRecords);
|
|
12755
|
+
}
|
|
12756
|
+
throw new FetchAllLimitError(limitMessage);
|
|
12757
|
+
}
|
|
12758
|
+
if (first.records.length < pageSize) return allRecords;
|
|
12759
|
+
windowOffset += pageSize;
|
|
12760
|
+
if (windowOffset >= KINTONE_MAX_OFFSET) {
|
|
12761
|
+
cursorId = getLastId(first.records);
|
|
12762
|
+
windowOffset = 0;
|
|
12706
12763
|
}
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12764
|
+
while (true) {
|
|
12765
|
+
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12766
|
+
return allRecords.slice(0, stopAfter);
|
|
12767
|
+
}
|
|
12768
|
+
if (allRecords.length >= maxRecords) {
|
|
12769
|
+
if (onLimit === "truncate") {
|
|
12770
|
+
if (!notified && options.onTruncate) {
|
|
12771
|
+
options.onTruncate(maxRecords);
|
|
12772
|
+
notified = true;
|
|
12773
|
+
}
|
|
12774
|
+
return allRecords.slice(0, maxRecords);
|
|
12775
|
+
}
|
|
12776
|
+
throw new FetchAllLimitError(limitMessage);
|
|
12777
|
+
}
|
|
12778
|
+
const remaining = fetchCap - allRecords.length;
|
|
12779
|
+
const maxPagesByLimit = Math.ceil(remaining / pageSize);
|
|
12780
|
+
const batchParallel = Math.max(1, Math.min(parallel, maxPagesByLimit));
|
|
12781
|
+
const batchOffsets = [];
|
|
12782
|
+
for (let i = 0; i < batchParallel; i++) {
|
|
12783
|
+
const off = windowOffset + i * pageSize;
|
|
12784
|
+
if (off >= KINTONE_MAX_OFFSET) break;
|
|
12785
|
+
batchOffsets.push(off);
|
|
12786
|
+
}
|
|
12787
|
+
const cq = buildCursorQuery(query, cursorId);
|
|
12788
|
+
const responses = await Promise.all(
|
|
12789
|
+
batchOffsets.map(
|
|
12790
|
+
(offset) => fetchPage(fetcher, app, cq, fetchFields, pageSize, offset)
|
|
12791
|
+
)
|
|
12792
|
+
);
|
|
12793
|
+
for (const response of responses) notifySearchAborted(response, options);
|
|
12794
|
+
let done = false;
|
|
12795
|
+
for (const res of responses) {
|
|
12796
|
+
allRecords.push(...res.records);
|
|
12797
|
+
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12798
|
+
return allRecords.slice(0, stopAfter);
|
|
12799
|
+
}
|
|
12800
|
+
if (allRecords.length > maxRecords) {
|
|
12801
|
+
if (onLimit === "truncate") {
|
|
12802
|
+
if (!notified && options.onTruncate) {
|
|
12803
|
+
options.onTruncate(maxRecords);
|
|
12804
|
+
notified = true;
|
|
12805
|
+
}
|
|
12806
|
+
return allRecords.slice(0, maxRecords);
|
|
12807
|
+
}
|
|
12808
|
+
throw new FetchAllLimitError(limitMessage);
|
|
12809
|
+
}
|
|
12810
|
+
if (res.records.length < pageSize) {
|
|
12811
|
+
done = true;
|
|
12812
|
+
break;
|
|
12813
|
+
}
|
|
12814
|
+
}
|
|
12815
|
+
if (done) break;
|
|
12816
|
+
windowOffset += pageSize * batchOffsets.length;
|
|
12817
|
+
if (windowOffset >= KINTONE_MAX_OFFSET) {
|
|
12818
|
+
const lastRes = responses[responses.length - 1];
|
|
12819
|
+
cursorId = getLastId(lastRes.records);
|
|
12820
|
+
windowOffset = 0;
|
|
12821
|
+
}
|
|
12712
12822
|
}
|
|
12713
|
-
return
|
|
12714
|
-
}
|
|
12715
|
-
function localExpression() {
|
|
12716
|
-
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12823
|
+
return allRecords;
|
|
12717
12824
|
}
|
|
12718
|
-
function
|
|
12719
|
-
|
|
12825
|
+
function notifySearchAborted(response, options) {
|
|
12826
|
+
if (response.searchAborted) options.onSearchAborted?.();
|
|
12720
12827
|
}
|
|
12721
|
-
function
|
|
12722
|
-
return
|
|
12723
|
-
|
|
12724
|
-
|
|
12828
|
+
function extractIds(records) {
|
|
12829
|
+
return records.map((r) => {
|
|
12830
|
+
const raw = r["$id"]?.value;
|
|
12831
|
+
if (raw === void 0) {
|
|
12832
|
+
throw new Error(
|
|
12833
|
+
'\u30EC\u30B3\u30FC\u30C9\u306B $id \u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093\u3002fields \u306B "$id" \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002'
|
|
12834
|
+
);
|
|
12835
|
+
}
|
|
12836
|
+
const id = Number(raw);
|
|
12837
|
+
if (!Number.isFinite(id)) {
|
|
12838
|
+
throw new Error(`$id \u306E\u5024\u304C\u6570\u5024\u3067\u306F\u3042\u308A\u307E\u305B\u3093: ${raw}`);
|
|
12839
|
+
}
|
|
12840
|
+
return id;
|
|
12725
12841
|
});
|
|
12726
12842
|
}
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12843
|
+
var PAGE_SIZE_DEFAULT = 500;
|
|
12844
|
+
var PARALLEL_DEFAULT = 1;
|
|
12845
|
+
var MAX_RECORDS_DEFAULT = 1e4;
|
|
12846
|
+
var KINTONE_MAX_OFFSET = 1e4;
|
|
12847
|
+
async function fetchPage(fetcher, app, query, fields, pageSize, offset) {
|
|
12848
|
+
const pageQuery = buildPageQuery(query, pageSize, offset);
|
|
12849
|
+
return fetcher({ app, query: pageQuery, fields });
|
|
12732
12850
|
}
|
|
12733
|
-
function
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12851
|
+
function buildCursorQuery(baseQuery, cursorId) {
|
|
12852
|
+
const base = baseQuery.trimEnd();
|
|
12853
|
+
if (cursorId <= 0) {
|
|
12854
|
+
return base ? `${base} order by $id asc` : "order by $id asc";
|
|
12855
|
+
}
|
|
12856
|
+
const cursor = `$id > ${cursorId} order by $id asc`;
|
|
12857
|
+
return base ? `(${base}) and ${cursor}` : cursor;
|
|
12737
12858
|
}
|
|
12738
|
-
function
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12859
|
+
function buildPageQuery(query, pageSize, offset) {
|
|
12860
|
+
const base = query.trimEnd();
|
|
12861
|
+
const suffix = `limit ${pageSize} offset ${offset}`;
|
|
12862
|
+
return base ? `${base} ${suffix}` : suffix;
|
|
12742
12863
|
}
|
|
12743
|
-
function
|
|
12744
|
-
|
|
12745
|
-
|
|
12864
|
+
function getLastId(records) {
|
|
12865
|
+
const last = records[records.length - 1];
|
|
12866
|
+
const raw = last?.["$id"]?.value;
|
|
12867
|
+
if (raw === void 0) return 0;
|
|
12868
|
+
const id = Number(raw);
|
|
12869
|
+
return Number.isFinite(id) ? id : 0;
|
|
12870
|
+
}
|
|
12871
|
+
var FetchAllLimitError = class extends Error {
|
|
12872
|
+
constructor(message, completeInputWrapped = false) {
|
|
12873
|
+
super(message);
|
|
12874
|
+
this.completeInputWrapped = completeInputWrapped;
|
|
12875
|
+
this.name = "FetchAllLimitError";
|
|
12746
12876
|
}
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12877
|
+
};
|
|
12878
|
+
|
|
12879
|
+
// src/core/optimization/sharedPlanner.ts
|
|
12880
|
+
async function fetchRecordsForSharedPlan(getRecords, app, query, fields, options) {
|
|
12881
|
+
const records = await fetchAll(getRecords, app, query, fields, {
|
|
12882
|
+
maxRecords: options.maxRecords,
|
|
12883
|
+
parallel: options.parallel,
|
|
12884
|
+
onLimit: options.onLimit ?? "error",
|
|
12885
|
+
onTruncate: options.onTruncate,
|
|
12886
|
+
onSearchAborted: options.onSearchAborted
|
|
12887
|
+
});
|
|
12750
12888
|
return {
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
...result.reasons,
|
|
12754
|
-
{
|
|
12755
|
-
code: "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN",
|
|
12756
|
-
functionName: relative.functionName,
|
|
12757
|
-
field: relative.field,
|
|
12758
|
-
fieldType: relative.fieldType,
|
|
12759
|
-
operator: relative.operator
|
|
12760
|
-
}
|
|
12761
|
-
]
|
|
12889
|
+
records,
|
|
12890
|
+
metrics: { fetchedRows: records.length }
|
|
12762
12891
|
};
|
|
12763
12892
|
}
|
|
12764
|
-
function
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
12893
|
+
async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
12894
|
+
const { records, metrics } = await fetchRecordsForSharedPlan(
|
|
12895
|
+
getRecords,
|
|
12896
|
+
app,
|
|
12897
|
+
query,
|
|
12898
|
+
["$id"],
|
|
12899
|
+
options
|
|
12772
12900
|
);
|
|
12773
12901
|
return {
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
...result.reasons,
|
|
12777
|
-
{
|
|
12778
|
-
code: "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN",
|
|
12779
|
-
functionName: legacy.functionName,
|
|
12780
|
-
field: legacy.field,
|
|
12781
|
-
fieldType: legacy.fieldType,
|
|
12782
|
-
operator: legacy.operator
|
|
12783
|
-
}
|
|
12784
|
-
]
|
|
12902
|
+
ids: extractIds(records),
|
|
12903
|
+
metrics
|
|
12785
12904
|
};
|
|
12786
12905
|
}
|
|
12787
|
-
function requireExactFunctionPushdown(result) {
|
|
12788
|
-
return requireExactLegacyKintoneFunctionPushdown(
|
|
12789
|
-
requireExactRelativeDatePushdown(result)
|
|
12790
|
-
);
|
|
12791
|
-
}
|
|
12792
|
-
|
|
12793
|
-
// src/core/optimization/joinDateTimeLiteralPolicy.ts
|
|
12794
|
-
function isCanonicalJoinDate(value) {
|
|
12795
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
12796
|
-
if (!match) return false;
|
|
12797
|
-
const year = Number(match[1]);
|
|
12798
|
-
const month = Number(match[2]);
|
|
12799
|
-
const day = Number(match[3]);
|
|
12800
|
-
if (year < 1 || year > 9999) return false;
|
|
12801
|
-
const date = /* @__PURE__ */ new Date(0);
|
|
12802
|
-
date.setUTCFullYear(year, month - 1, day);
|
|
12803
|
-
date.setUTCHours(0, 0, 0, 0);
|
|
12804
|
-
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
12805
|
-
}
|
|
12806
|
-
function isCanonicalJoinTime(value) {
|
|
12807
|
-
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
12808
|
-
return match !== null && Number(match[1]) <= 23 && Number(match[2]) <= 59;
|
|
12809
|
-
}
|
|
12810
|
-
function isCanonicalJoinDateTime(value) {
|
|
12811
|
-
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.exec(value);
|
|
12812
|
-
if (!match || !isCanonicalJoinDate(match[1])) return false;
|
|
12813
|
-
return Number(match[2]) <= 23 && Number(match[3]) <= 59 && Number(match[4]) <= 59;
|
|
12814
|
-
}
|
|
12815
12906
|
|
|
12816
12907
|
// src/core/optimization/joinNumberLiteralPolicy.ts
|
|
12817
12908
|
function isJoinNumberLiteralSupported(literal) {
|
|
@@ -12851,7 +12942,7 @@ var KLIKE_TYPES = /* @__PURE__ */ new Set([
|
|
|
12851
12942
|
"RICH_TEXT",
|
|
12852
12943
|
"FILE"
|
|
12853
12944
|
]);
|
|
12854
|
-
var
|
|
12945
|
+
var DATETIME_TYPES2 = /* @__PURE__ */ new Set([
|
|
12855
12946
|
"DATETIME",
|
|
12856
12947
|
"CREATED_TIME",
|
|
12857
12948
|
"UPDATED_TIME"
|
|
@@ -13477,7 +13568,7 @@ function classifySupportedLeaf(predicate, owner, fieldType) {
|
|
|
13477
13568
|
}
|
|
13478
13569
|
return (predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>") && predicate.right.type === "STRING" && predicate.right.value !== "" ? "exact" : "unsafe";
|
|
13479
13570
|
}
|
|
13480
|
-
if (fieldType === "DATE" || fieldType === "TIME" ||
|
|
13571
|
+
if (fieldType === "DATE" || fieldType === "TIME" || DATETIME_TYPES2.has(fieldType)) {
|
|
13481
13572
|
if (predicate.op !== "=" && predicate.op !== "!=" && predicate.op !== "<>" && predicate.op !== "<" && predicate.op !== ">" && predicate.op !== "<=" && predicate.op !== ">=" || predicate.right.type !== "STRING") return "unsafe";
|
|
13482
13573
|
if (fieldType === "DATE") {
|
|
13483
13574
|
return isCanonicalJoinDate(predicate.right.value) ? "exact" : "unsafe";
|
|
@@ -20245,6 +20336,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
20245
20336
|
parallel,
|
|
20246
20337
|
options.onLimitReached ?? "error",
|
|
20247
20338
|
warnings,
|
|
20339
|
+
cacheContext,
|
|
20340
|
+
cteCache,
|
|
20248
20341
|
null,
|
|
20249
20342
|
plainGroupByPlan,
|
|
20250
20343
|
""
|
|
@@ -20633,6 +20726,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
20633
20726
|
parallel,
|
|
20634
20727
|
effectiveOptions.onLimitReached ?? "error",
|
|
20635
20728
|
warnings,
|
|
20729
|
+
cacheContext,
|
|
20730
|
+
cteCache,
|
|
20636
20731
|
pushDownCond,
|
|
20637
20732
|
plainGroupByPlan
|
|
20638
20733
|
);
|
|
@@ -20825,10 +20920,9 @@ function splitChunks(items, size) {
|
|
|
20825
20920
|
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
20826
20921
|
return out;
|
|
20827
20922
|
}
|
|
20828
|
-
var JOIN_IN_CHUNK_SIZE = 50;
|
|
20829
20923
|
var JOIN_IN_MAX_CHUNKS = 6;
|
|
20830
|
-
var JOIN_IN_MAX_KEYS =
|
|
20831
|
-
async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxRecords, parallel, onLimit, warnings, pushDownCond = null, plainGroupByPlan, additionalPushQuery = "") {
|
|
20924
|
+
var JOIN_IN_MAX_KEYS = JOIN_KEY_IN_CHUNK_SIZE * JOIN_IN_MAX_CHUNKS;
|
|
20925
|
+
async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxRecords, parallel, onLimit, warnings, cacheContext, materializedTables, pushDownCond = null, plainGroupByPlan, additionalPushQuery = "") {
|
|
20832
20926
|
if (join2.type !== "INNER") return null;
|
|
20833
20927
|
if (!join2.table.alias) return null;
|
|
20834
20928
|
if (join2.table.subtableCode) return null;
|
|
@@ -20851,17 +20945,47 @@ async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxR
|
|
|
20851
20945
|
}
|
|
20852
20946
|
const sourceRows2 = tables.get(sourceAlias);
|
|
20853
20947
|
if (!sourceRows2) return null;
|
|
20854
|
-
const
|
|
20948
|
+
const sourceTable = [stmt.from, ...stmt.joins.map((candidate) => candidate.table)].find((table) => effectiveTableAlias(table) === sourceAlias);
|
|
20949
|
+
const targetInfo = (await getFieldsCached(join2.table.appId, client, cacheContext)).find((info) => info.code === fieldCodeForTypeLookup(join2.table, joinField));
|
|
20950
|
+
const targetMeta = targetInfo ? materializedMetaFromFieldInfo(targetInfo, join2.table.appId) : systemColumnMeta(joinField);
|
|
20951
|
+
let sourceMeta;
|
|
20952
|
+
if (sourceTable?.cteName !== null && sourceTable?.cteName !== void 0) {
|
|
20953
|
+
sourceMeta = materializedTables?.get(sourceTable.cteName)?.columnMeta?.get(sourceField);
|
|
20954
|
+
} else if (sourceTable) {
|
|
20955
|
+
const sourceInfo = (await getFieldsCached(sourceTable.appId, client, cacheContext)).find((info) => info.code === fieldCodeForTypeLookup(sourceTable, sourceField));
|
|
20956
|
+
sourceMeta = sourceInfo ? materializedMetaFromFieldInfo(sourceInfo, sourceTable.appId) : systemColumnMeta(sourceField);
|
|
20957
|
+
}
|
|
20958
|
+
const keys = [];
|
|
20959
|
+
let hasEmptyValue = false;
|
|
20855
20960
|
for (const row of sourceRows2) {
|
|
20856
20961
|
const raw = row[sourceField]?.value;
|
|
20857
|
-
const txt = toScalarText(raw)
|
|
20858
|
-
if (txt.length
|
|
20962
|
+
const txt = toScalarText(raw);
|
|
20963
|
+
if (raw === null || raw === void 0 || txt.length === 0) hasEmptyValue = true;
|
|
20964
|
+
keys.push(txt);
|
|
20965
|
+
}
|
|
20966
|
+
const keyPlan = planJoinKeyPrefilter({
|
|
20967
|
+
fieldType: targetMeta?.fieldType,
|
|
20968
|
+
sourceSemantics: sourceMeta?.semantics,
|
|
20969
|
+
sourceRowCount: sourceRows2.length,
|
|
20970
|
+
values: keys,
|
|
20971
|
+
hasEmptyValue,
|
|
20972
|
+
maxInKeys: JOIN_IN_MAX_KEYS
|
|
20973
|
+
});
|
|
20974
|
+
if (keyPlan.kind === "EMPTY_SOURCE") return [];
|
|
20975
|
+
if (keyPlan.kind === "FALLBACK") {
|
|
20976
|
+
if (keyPlan.reason === "JOIN_KEY_LIMIT_EXCEEDED") {
|
|
20977
|
+
const count = new Set(keys).size;
|
|
20978
|
+
warnings.add(
|
|
20979
|
+
`JOIN\u30AD\u30FC\u304C ${count} \u4EF6\u306E\u305F\u3081 ON \u6700\u9069\u5316\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u3001JOIN\u5148\u3092\u5168\u4EF6\u53D6\u5F97\u3057\u307E\u3059\uFF08\u4E0A\u9650 ${JOIN_IN_MAX_KEYS} \u4EF6\uFF09\u3002`
|
|
20980
|
+
);
|
|
20981
|
+
}
|
|
20982
|
+
return null;
|
|
20859
20983
|
}
|
|
20860
|
-
|
|
20861
|
-
|
|
20862
|
-
if (
|
|
20984
|
+
if (keyPlan.kind === "RANGE_CANDIDATE") return null;
|
|
20985
|
+
const prefilterQueries = buildJoinKeyPrefilterQueries(keyPlan, joinField, sqlQuote);
|
|
20986
|
+
if (prefilterQueries.length === 0) {
|
|
20863
20987
|
warnings.add(
|
|
20864
|
-
`JOIN\u30AD\u30FC\u304C
|
|
20988
|
+
`JOIN\u30AD\u30FC\u304C 0 \u4EF6\u306E\u305F\u3081 ON \u6700\u9069\u5316\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u3001JOIN\u5148\u3092\u5168\u4EF6\u53D6\u5F97\u3057\u307E\u3059\uFF08\u4E0A\u9650 ${JOIN_IN_MAX_KEYS} \u4EF6\uFF09\u3002`
|
|
20865
20989
|
);
|
|
20866
20990
|
return null;
|
|
20867
20991
|
}
|
|
@@ -20870,18 +20994,16 @@ async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxR
|
|
|
20870
20994
|
warnings.add(`\u53D6\u5F97\u4E0A\u9650\uFF08${max} \u4EF6\uFF09\u306B\u9054\u3057\u305F\u305F\u3081\u3001${max} \u4EF6\u3067\u6253\u3061\u5207\u3063\u3066\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002`);
|
|
20871
20995
|
markLimitReached(client, join2.table.appId);
|
|
20872
20996
|
};
|
|
20873
|
-
const chunks = splitChunks(values, JOIN_IN_CHUNK_SIZE);
|
|
20874
20997
|
const merged = [];
|
|
20875
20998
|
const seen = /* @__PURE__ */ new Set();
|
|
20876
|
-
for (const
|
|
20877
|
-
const inClause = `${joinField} in (${chunk3.map(sqlQuote).join(",")})`;
|
|
20999
|
+
for (const joinKeyQuery of prefilterQueries) {
|
|
20878
21000
|
const pushQueries = [
|
|
20879
21001
|
pushDownCond !== null ? whereToKintone(pushDownCond) : "",
|
|
20880
21002
|
additionalPushQuery
|
|
20881
21003
|
].filter((query2) => query2 !== "");
|
|
20882
21004
|
const query = pushQueries.reduce(
|
|
20883
21005
|
(combined, pushQuery) => `(${combined}) and (${pushQuery})`,
|
|
20884
|
-
|
|
21006
|
+
joinKeyQuery
|
|
20885
21007
|
);
|
|
20886
21008
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, join2.table.appId, query, fields, {
|
|
20887
21009
|
parallel,
|
|
@@ -24121,6 +24243,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
24121
24243
|
return cache;
|
|
24122
24244
|
}
|
|
24123
24245
|
var explainJoinPushdownPlans = /* @__PURE__ */ new WeakMap();
|
|
24246
|
+
var explainJoinKeyPrefilters = /* @__PURE__ */ new WeakMap();
|
|
24124
24247
|
var explainChoiceEqualityRewrites = /* @__PURE__ */ new WeakMap();
|
|
24125
24248
|
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
24126
24249
|
var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
|
|
@@ -24152,6 +24275,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24152
24275
|
(node) => node.source === source || JSON.stringify(node.source) === JSON.stringify(source)
|
|
24153
24276
|
);
|
|
24154
24277
|
const explainRelations = new Map(initialRelations ?? []);
|
|
24278
|
+
const staticExplainRelations = new Set(initialRelations?.keys() ?? []);
|
|
24155
24279
|
const explainSourceColumns = async (select) => {
|
|
24156
24280
|
const tables = [select.from, ...select.joins.map((join2) => join2.table)];
|
|
24157
24281
|
if (tables.length > 1 && select.columns.some(
|
|
@@ -24231,6 +24355,17 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24231
24355
|
const withStatement = node;
|
|
24232
24356
|
for (const cte of withStatement.ctes) {
|
|
24233
24357
|
await preflightExplainRelations(cte.query);
|
|
24358
|
+
if (cte.query.type === "GENERATE_SERIES") {
|
|
24359
|
+
const generated = executeGenerateSeries(cte.query);
|
|
24360
|
+
explainRelations.set(cte.name, {
|
|
24361
|
+
rows: generated.rows,
|
|
24362
|
+
columns: generated.columns,
|
|
24363
|
+
columnMeta: materializedMetaBySelectResult.get(generated),
|
|
24364
|
+
uniqueGeneratedColumn: cte.query.columnAlias
|
|
24365
|
+
});
|
|
24366
|
+
staticExplainRelations.add(cte.name);
|
|
24367
|
+
continue;
|
|
24368
|
+
}
|
|
24234
24369
|
const columns = await inferExplainRelationColumns(cte.query);
|
|
24235
24370
|
explainRelations.set(cte.name, { rows: [], columns });
|
|
24236
24371
|
}
|
|
@@ -24311,6 +24446,71 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24311
24446
|
await loadTypedPushdownMeta(select, tracedClient, cacheContext)
|
|
24312
24447
|
);
|
|
24313
24448
|
if (joinPushdownPlan) explainJoinPushdownPlans.set(select, joinPushdownPlan);
|
|
24449
|
+
const joinKeyPlans = /* @__PURE__ */ new Map();
|
|
24450
|
+
for (const join2 of select.joins) {
|
|
24451
|
+
const joinAlias = join2.table.alias;
|
|
24452
|
+
if (join2.type !== "INNER" || !joinAlias || join2.table.subtableCode) continue;
|
|
24453
|
+
const leftAlias = join2.on.left.tableAlias;
|
|
24454
|
+
const rightAlias = join2.on.right.tableAlias;
|
|
24455
|
+
if (!leftAlias || !rightAlias) continue;
|
|
24456
|
+
const sourceAlias = leftAlias === joinAlias && rightAlias !== joinAlias ? rightAlias : rightAlias === joinAlias && leftAlias !== joinAlias ? leftAlias : void 0;
|
|
24457
|
+
const sourceField = leftAlias === joinAlias ? join2.on.right.field : join2.on.left.field;
|
|
24458
|
+
const joinField = leftAlias === joinAlias ? join2.on.left.field : join2.on.right.field;
|
|
24459
|
+
if (!sourceAlias) continue;
|
|
24460
|
+
const sourceTable = [select.from, ...select.joins.map((candidate) => candidate.table)].find((table) => effectiveTableAlias(table) === sourceAlias);
|
|
24461
|
+
const targetInfos = await getFieldsCached(join2.table.appId, tracedClient, cacheContext);
|
|
24462
|
+
const targetInfo = targetInfos.find((info) => info.code === fieldCodeForTypeLookup(join2.table, joinField));
|
|
24463
|
+
const targetMeta = targetInfo ? materializedMetaFromFieldInfo(targetInfo, join2.table.appId) : systemColumnMeta(joinField);
|
|
24464
|
+
let sourceMeta;
|
|
24465
|
+
let values;
|
|
24466
|
+
let sourceRowCount;
|
|
24467
|
+
let hasEmptyValue;
|
|
24468
|
+
if (sourceTable?.cteName !== null && sourceTable?.cteName !== void 0) {
|
|
24469
|
+
const relation = explainRelations.get(sourceTable.cteName);
|
|
24470
|
+
sourceMeta = relation?.columnMeta?.get(sourceField);
|
|
24471
|
+
if (staticExplainRelations.has(sourceTable.cteName) && relation) {
|
|
24472
|
+
sourceRowCount = relation.rows.length;
|
|
24473
|
+
values = relation.rows.map((row) => toScalarText(row[sourceField]));
|
|
24474
|
+
hasEmptyValue = values.some((value) => value.length === 0);
|
|
24475
|
+
}
|
|
24476
|
+
} else if (sourceTable) {
|
|
24477
|
+
const info = (await getFieldsCached(sourceTable.appId, tracedClient, cacheContext)).find((candidate) => candidate.code === fieldCodeForTypeLookup(sourceTable, sourceField));
|
|
24478
|
+
sourceMeta = info ? materializedMetaFromFieldInfo(info, sourceTable.appId) : systemColumnMeta(sourceField);
|
|
24479
|
+
}
|
|
24480
|
+
const plan = planJoinKeyPrefilter({
|
|
24481
|
+
fieldType: targetMeta?.fieldType,
|
|
24482
|
+
sourceSemantics: sourceMeta?.semantics,
|
|
24483
|
+
sourceRowCount,
|
|
24484
|
+
values,
|
|
24485
|
+
hasEmptyValue,
|
|
24486
|
+
maxInKeys: JOIN_IN_MAX_KEYS
|
|
24487
|
+
});
|
|
24488
|
+
if (plan.kind === "FALLBACK" && plan.reason === "JOIN_KEY_VALUES_RUNTIME") {
|
|
24489
|
+
const runtimePlanConsumesJoin = joinPushdownPlan?.joinPlan.items.some(
|
|
24490
|
+
(item) => item.targetAlias === joinAlias
|
|
24491
|
+
) || joinPushdownPlan?.joinPlan.serverFunctionConsumptions.some(
|
|
24492
|
+
(consumption) => consumption.targetAlias === joinAlias
|
|
24493
|
+
);
|
|
24494
|
+
if (runtimePlanConsumesJoin) continue;
|
|
24495
|
+
}
|
|
24496
|
+
const queries = buildJoinKeyPrefilterQueries(plan, joinField, sqlQuote);
|
|
24497
|
+
const additionalCandidate = select.where ? extractTypedPushdownCandidates(select.where, { tableAlias: joinAlias }) : null;
|
|
24498
|
+
let additionalQuery;
|
|
24499
|
+
let additionalRelation;
|
|
24500
|
+
if (additionalCandidate) {
|
|
24501
|
+
const infoByCode = new Map(targetInfos.map((info) => [info.code, info]));
|
|
24502
|
+
const additionalCapability = classifyWhereCapability(additionalCandidate, (field) => {
|
|
24503
|
+
const info = infoByCode.get(field.field);
|
|
24504
|
+
return info?.semantics ?? (info ? resolveFieldSemantics(info) : systemColumnMeta(field.field)?.semantics);
|
|
24505
|
+
});
|
|
24506
|
+
if (additionalCapability.capability === "EXACT_PUSHDOWN" || additionalCapability.capability === "SUPERSET_PREFILTER") {
|
|
24507
|
+
additionalQuery = whereToKintone(additionalCandidate);
|
|
24508
|
+
additionalRelation = additionalCapability.capability === "EXACT_PUSHDOWN" ? "exact" : "superset";
|
|
24509
|
+
}
|
|
24510
|
+
}
|
|
24511
|
+
joinKeyPlans.set(joinAlias, { plan, queries, additionalQuery, additionalRelation });
|
|
24512
|
+
}
|
|
24513
|
+
if (joinKeyPlans.size > 0) explainJoinKeyPrefilters.set(select, joinKeyPlans);
|
|
24314
24514
|
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
24315
24515
|
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
24316
24516
|
if (select.orderMode !== "KINTONE_NATIVE") {
|
|
@@ -25427,9 +25627,13 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25427
25627
|
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
25428
25628
|
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
25429
25629
|
const joinBoundQuery = join2.table.alias ? runtimeJoinPlan?.queriesByAlias.get(join2.table.alias) : void 0;
|
|
25430
|
-
const
|
|
25630
|
+
const joinKey = join2.table.alias ? explainJoinKeyPrefilters.get(stmt)?.get(join2.table.alias) : void 0;
|
|
25631
|
+
const baseJoinQ = joinBoundQuery || (joinPushDown !== null ? whereToKintone(joinPushDown) : joinKey?.additionalQuery ? joinKey.additionalQuery : "(\u5168\u4EF6\u53D6\u5F97)");
|
|
25632
|
+
const runtimeJoinKeyCandidate = joinKey?.plan.kind === "RANGE_CANDIDATE" || joinKey?.plan.kind === "FALLBACK" && joinKey.plan.reason === "JOIN_KEY_VALUES_RUNTIME";
|
|
25633
|
+
const joinQueries = joinKey && joinKey.queries.length > 0 ? joinKey.queries.map((query) => baseJoinQ !== "(\u5168\u4EF6\u53D6\u5F97)" ? `(${query}) and (${baseJoinQ})` : query) : [runtimeJoinKeyCandidate ? "(runtime source keys)" : baseJoinQ];
|
|
25634
|
+
const joinQ = joinQueries.join(" | ");
|
|
25431
25635
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
25432
|
-
lines.push(` kintone query: ${
|
|
25636
|
+
for (const query of joinQueries) lines.push(` kintone query: ${query}`);
|
|
25433
25637
|
const joinPlanItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
25434
25638
|
(item) => item.targetAlias === join2.table.alias
|
|
25435
25639
|
);
|
|
@@ -25437,8 +25641,8 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25437
25641
|
(consumption) => consumption.targetAlias === join2.table.alias
|
|
25438
25642
|
);
|
|
25439
25643
|
if (emitFetch && join2.table.cteName === null) {
|
|
25440
|
-
const joinPending = !runtimeJoinPlan && joinCandidate !== null;
|
|
25441
|
-
const joinFetchScope = joinQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : joinPending ? "PREFILTERED" : joinPlanItem?.relation === "exact" || joinFunctionConsumption ? "EXACT" : "PREFILTERED";
|
|
25644
|
+
const joinPending = runtimeJoinKeyCandidate || !joinKey && !runtimeJoinPlan && joinCandidate !== null;
|
|
25645
|
+
const joinFetchScope = joinQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : joinPending ? "PREFILTERED" : joinKey?.plan.kind === "IN" && joinPlanItem?.relation !== "superset" && joinKey.additionalRelation !== "superset" ? "EXACT" : joinPlanItem?.relation === "exact" || joinFunctionConsumption ? "EXACT" : "PREFILTERED";
|
|
25442
25646
|
lines.push(renderFetchScope(createExplainFetchSource(
|
|
25443
25647
|
collector,
|
|
25444
25648
|
join2.table.appId,
|
|
@@ -25449,7 +25653,25 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25449
25653
|
joinPending
|
|
25450
25654
|
)));
|
|
25451
25655
|
}
|
|
25452
|
-
if (
|
|
25656
|
+
if (joinKey) {
|
|
25657
|
+
if (joinKey.plan.kind === "RANGE") {
|
|
25658
|
+
lines.push(" join key prefilter: range");
|
|
25659
|
+
for (const query of joinQueries) lines.push(` pushdown applied: ${query}`);
|
|
25660
|
+
lines.push(" relation: superset");
|
|
25661
|
+
} else if (joinKey.plan.kind === "IN") {
|
|
25662
|
+
const relation = joinPlanItem?.relation === "superset" || joinKey.additionalRelation === "superset" ? "superset" : "exact";
|
|
25663
|
+
lines.push(" join key prefilter: in");
|
|
25664
|
+
for (const query of joinQueries) lines.push(` pushdown applied: ${query}`);
|
|
25665
|
+
lines.push(` relation: ${relation}`);
|
|
25666
|
+
} else if (joinKey.plan.kind === "RANGE_CANDIDATE") {
|
|
25667
|
+
lines.push(" join key prefilter: range candidate");
|
|
25668
|
+
lines.push(" relation: superset");
|
|
25669
|
+
lines.push(` join key prefilter reason: ${joinKey.plan.reason}`);
|
|
25670
|
+
} else if (joinKey.plan.kind === "FALLBACK") {
|
|
25671
|
+
lines.push(joinKey.plan.reason === "JOIN_KEY_VALUES_RUNTIME" ? " join key prefilter: runtime candidate" : " join key prefilter: not applied");
|
|
25672
|
+
lines.push(` join key prefilter reason: ${joinKey.plan.reason}`);
|
|
25673
|
+
}
|
|
25674
|
+
} else if (joinPlanItem || joinFunctionConsumption) {
|
|
25453
25675
|
lines.push(` pushdown applied: ${joinBoundQuery}`);
|
|
25454
25676
|
lines.push(` relation: ${joinPlanItem?.relation ?? "exact"}`);
|
|
25455
25677
|
} else if (!runtimeJoinPlan && joinCandidate !== null) {
|