@rex0220/kintone-sql-tools 3.59.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 +968 -675
- 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 +81 -15
- package/dist-engine/meta/esm.json +81 -15
- package/dist-engine/meta/umd.json +81 -15
- package/dist-mcp/ksql-mcp.js +598 -300
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -3187,7 +3187,7 @@ var Parser = class {
|
|
|
3187
3187
|
return { type: "BINARY", op: "KLIKE", left: field, right: pattern };
|
|
3188
3188
|
}
|
|
3189
3189
|
const op = this.parseCompareOp();
|
|
3190
|
-
const right = this.parseWhereSqlValue();
|
|
3190
|
+
const right = this.parseWhereSqlValue(op !== "LIKE");
|
|
3191
3191
|
return { type: "BINARY", op, left: field, right };
|
|
3192
3192
|
}
|
|
3193
3193
|
parseCompareOp() {
|
|
@@ -3310,8 +3310,13 @@ var Parser = class {
|
|
|
3310
3310
|
return { type: "FIELD", tableAlias: qi.tableAlias, field: qi.field };
|
|
3311
3311
|
}
|
|
3312
3312
|
// 右辺の値
|
|
3313
|
-
parseWhereSqlValue() {
|
|
3313
|
+
parseWhereSqlValue(allowUnaryPlusNumberLiteral = true) {
|
|
3314
3314
|
const tok = this.peek();
|
|
3315
|
+
if (allowUnaryPlusNumberLiteral && tok.kind === "+" /* PLUS */) {
|
|
3316
|
+
this.advance();
|
|
3317
|
+
const number = this.expect("NUMBER" /* NUMBER */, "\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
3318
|
+
return makeNumberLiteral(`+${number.value}`);
|
|
3319
|
+
}
|
|
3315
3320
|
if (this.allowRelativeDateFunctions && tok.kind === "IDENT" /* IDENT */ && this.peekAt(1).kind === "(" /* LPAREN */ && isRelativeDateFunctionName(tok.value.toUpperCase())) {
|
|
3316
3321
|
return this.parseRelativeDateFunction();
|
|
3317
3322
|
}
|
|
@@ -12037,414 +12042,191 @@ function isOuterJoinNonPreservedTable(statement, table, isMainTable) {
|
|
|
12037
12042
|
return false;
|
|
12038
12043
|
}
|
|
12039
12044
|
|
|
12040
|
-
// src/core/
|
|
12041
|
-
function
|
|
12042
|
-
const
|
|
12043
|
-
if (
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
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;
|
|
12052
12057
|
}
|
|
12053
|
-
function
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
case "BINARY":
|
|
12057
|
-
return valueNeedsFieldMetadata(where.left);
|
|
12058
|
-
case "NULL_CHECK":
|
|
12059
|
-
return valueNeedsFieldMetadata(where.field);
|
|
12060
|
-
case "LOGICAL":
|
|
12061
|
-
return whereNeedsFieldMetadata(where.left) || whereNeedsFieldMetadata(where.right);
|
|
12062
|
-
case "NOT":
|
|
12063
|
-
case "GROUP":
|
|
12064
|
-
return whereNeedsFieldMetadata(where.expr);
|
|
12065
|
-
case "EXISTS":
|
|
12066
|
-
case "BOOLEAN":
|
|
12067
|
-
return false;
|
|
12068
|
-
}
|
|
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;
|
|
12069
12061
|
}
|
|
12070
|
-
function
|
|
12071
|
-
|
|
12072
|
-
if (
|
|
12073
|
-
|
|
12074
|
-
if (item["type"] === "FIELD") return item["field"] !== "$id";
|
|
12075
|
-
if (item["type"] === "SELECT") return false;
|
|
12076
|
-
return Object.values(item).some(valueNeedsFieldMetadata);
|
|
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;
|
|
12077
12066
|
}
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12067
|
+
|
|
12068
|
+
// src/core/optimization/whereCapability.ts
|
|
12069
|
+
var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
|
|
12070
|
+
var RELATIVE_DATE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
12071
|
+
"DATE",
|
|
12072
|
+
"DATETIME",
|
|
12073
|
+
"CREATED_TIME",
|
|
12074
|
+
"UPDATED_TIME"
|
|
12075
|
+
]);
|
|
12076
|
+
var RELATIVE_DATE_OPERATORS = new Set(RANGE_AND_EQUALITY);
|
|
12077
|
+
var LEGACY_KINTONE_FUNCTION_FIELD_TYPES = /* @__PURE__ */ new Map([
|
|
12078
|
+
["TODAY", /* @__PURE__ */ new Set(["DATE", "DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
12079
|
+
["NOW", /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
12080
|
+
["LOGINUSER", /* @__PURE__ */ new Set(["CREATOR", "MODIFIER", "USER_SELECT"])],
|
|
12081
|
+
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["ORGANIZATION_SELECT"])]
|
|
12082
|
+
]);
|
|
12083
|
+
var LEGACY_KINTONE_FUNCTION_OPERATORS = /* @__PURE__ */ new Map([
|
|
12084
|
+
["TODAY", new Set(RANGE_AND_EQUALITY)],
|
|
12085
|
+
["NOW", new Set(RANGE_AND_EQUALITY)],
|
|
12086
|
+
["LOGINUSER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12087
|
+
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12088
|
+
]);
|
|
12089
|
+
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
12090
|
+
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
12091
|
+
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12092
|
+
["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12093
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12094
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12095
|
+
["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12096
|
+
["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12097
|
+
["DATE", new Set(RANGE_AND_EQUALITY)],
|
|
12098
|
+
["TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12099
|
+
["DATETIME", new Set(RANGE_AND_EQUALITY)],
|
|
12100
|
+
["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
12101
|
+
["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
12102
|
+
["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12103
|
+
["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12104
|
+
["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12105
|
+
["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12106
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12107
|
+
["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12108
|
+
["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12109
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12110
|
+
["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12111
|
+
["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12112
|
+
["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12113
|
+
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12114
|
+
["STATUS", new Set(EQUALITY_IN)],
|
|
12115
|
+
["STATUS_ASSIGNEE", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12116
|
+
]);
|
|
12117
|
+
var LOCAL_VALID_OPERATORS = /* @__PURE__ */ new Map([
|
|
12118
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12119
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12120
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12121
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12122
|
+
]);
|
|
12123
|
+
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
12124
|
+
"RECORD_NUMBER",
|
|
12125
|
+
"__ID__",
|
|
12126
|
+
"CREATOR",
|
|
12127
|
+
"MODIFIER",
|
|
12128
|
+
"CREATED_TIME",
|
|
12129
|
+
"UPDATED_TIME",
|
|
12130
|
+
"DATE",
|
|
12131
|
+
"TIME",
|
|
12132
|
+
"DATETIME",
|
|
12133
|
+
"SINGLE_LINE_TEXT",
|
|
12134
|
+
"LINK",
|
|
12135
|
+
"NUMBER",
|
|
12136
|
+
"CALC",
|
|
12137
|
+
"MULTI_LINE_TEXT",
|
|
12138
|
+
"RICH_TEXT",
|
|
12139
|
+
"RADIO_BUTTON",
|
|
12140
|
+
"DROP_DOWN",
|
|
12141
|
+
"STATUS",
|
|
12142
|
+
// 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
|
|
12143
|
+
"KSQL_STRING",
|
|
12144
|
+
"KSQL_NUMBER",
|
|
12145
|
+
"KSQL_BOOLEAN"
|
|
12146
|
+
]);
|
|
12147
|
+
var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
12148
|
+
"CHECK_BOX",
|
|
12149
|
+
"MULTI_SELECT",
|
|
12150
|
+
"FILE",
|
|
12151
|
+
"USER_SELECT",
|
|
12152
|
+
"ORGANIZATION_SELECT",
|
|
12153
|
+
"GROUP_SELECT",
|
|
12154
|
+
"STATUS_ASSIGNEE",
|
|
12155
|
+
"CATEGORY"
|
|
12156
|
+
]);
|
|
12157
|
+
function nativeWhereOperatorsForType(fieldType) {
|
|
12158
|
+
return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
|
|
12082
12159
|
}
|
|
12083
|
-
function
|
|
12084
|
-
const
|
|
12160
|
+
function normalizeChoiceEquality(where, resolveField2) {
|
|
12161
|
+
const rewrites = [];
|
|
12085
12162
|
const visit = (node) => {
|
|
12086
|
-
if (node ===
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12163
|
+
if (node.type === "BINARY") {
|
|
12164
|
+
if ((node.op === "=" || node.op === "!=" || node.op === "<>") && node.left.type === "FIELD" && node.right.type === "STRING" && node.right.value !== "") {
|
|
12165
|
+
const semantics = resolveField2(node.left);
|
|
12166
|
+
if (semantics !== void 0 && semantics.compareMode === "option" && LOCAL_SCALAR_TYPES.has(semantics.fieldType) && nativeWhereOperatorsForType(semantics.fieldType).has("in") && semantics.optionOrder?.has(node.right.value) === true) {
|
|
12167
|
+
const normalizedOperator = node.op === "=" ? "IN" : "NOT_IN";
|
|
12168
|
+
rewrites.push({
|
|
12169
|
+
field: node.left,
|
|
12170
|
+
originalOperator: node.op,
|
|
12171
|
+
normalizedOperator,
|
|
12172
|
+
value: node.right.value
|
|
12173
|
+
});
|
|
12174
|
+
return {
|
|
12175
|
+
...node,
|
|
12176
|
+
op: normalizedOperator,
|
|
12177
|
+
right: { type: "IN_LIST", values: [node.right] }
|
|
12178
|
+
};
|
|
12179
|
+
}
|
|
12180
|
+
}
|
|
12181
|
+
return node;
|
|
12094
12182
|
}
|
|
12095
|
-
if (
|
|
12096
|
-
|
|
12183
|
+
if (node.type === "LOGICAL") {
|
|
12184
|
+
const left = visit(node.left);
|
|
12185
|
+
const right = visit(node.right);
|
|
12186
|
+
return left === node.left && right === node.right ? node : { ...node, left, right };
|
|
12097
12187
|
}
|
|
12098
|
-
if (
|
|
12099
|
-
|
|
12188
|
+
if (node.type === "GROUP" || node.type === "NOT") {
|
|
12189
|
+
const expr = visit(node.expr);
|
|
12190
|
+
return expr === node.expr ? node : { ...node, expr };
|
|
12191
|
+
}
|
|
12192
|
+
return node;
|
|
12100
12193
|
};
|
|
12101
|
-
return visit(
|
|
12194
|
+
return { normalizedWhere: visit(where), rewrites };
|
|
12102
12195
|
}
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
const pageSize = options.pageSize ?? PAGE_SIZE_DEFAULT;
|
|
12107
|
-
const parallel = Math.max(1, options.parallel ?? PARALLEL_DEFAULT);
|
|
12108
|
-
const maxRecords = options.maxRecords ?? MAX_RECORDS_DEFAULT;
|
|
12109
|
-
const onLimit = options.onLimit ?? "error";
|
|
12110
|
-
const stopAfter = options.stopAfter;
|
|
12111
|
-
if (stopAfter !== void 0 && (!Number.isSafeInteger(stopAfter) || stopAfter <= 0 || stopAfter > maxRecords)) {
|
|
12112
|
-
throw new RangeError("stopAfter must be a positive safe integer <= maxRecords");
|
|
12113
|
-
}
|
|
12114
|
-
const fetchCap = stopAfter ?? maxRecords;
|
|
12115
|
-
const fetchFields = fields.length > 0 && !fields.includes("$id") ? [...fields, "$id"] : fields;
|
|
12116
|
-
const allRecords = [];
|
|
12117
|
-
let notified = false;
|
|
12118
|
-
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`;
|
|
12119
|
-
let cursorId = 0;
|
|
12120
|
-
let windowOffset = 0;
|
|
12121
|
-
const cursorQuery0 = buildCursorQuery(query, cursorId);
|
|
12122
|
-
const first = await fetchPage(fetcher, app, cursorQuery0, fetchFields, pageSize, windowOffset);
|
|
12123
|
-
notifySearchAborted(first, options);
|
|
12124
|
-
allRecords.push(...first.records);
|
|
12125
|
-
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12126
|
-
return allRecords.slice(0, stopAfter);
|
|
12196
|
+
function classifyWhereCapability(where, resolveField2) {
|
|
12197
|
+
if (where === null) {
|
|
12198
|
+
return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
|
|
12127
12199
|
}
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12200
|
+
return classifyNode(where, resolveField2);
|
|
12201
|
+
}
|
|
12202
|
+
function classifyNode(where, resolveField2) {
|
|
12203
|
+
switch (where.type) {
|
|
12204
|
+
case "BOOLEAN":
|
|
12205
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12206
|
+
case "BINARY":
|
|
12207
|
+
return classifyBinary(where.op, where.left, where.right, resolveField2);
|
|
12208
|
+
case "NULL_CHECK":
|
|
12209
|
+
if (where.field.type !== "FIELD") return localExpression();
|
|
12210
|
+
return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
|
|
12211
|
+
case "EXISTS":
|
|
12212
|
+
return localExpression();
|
|
12213
|
+
case "GROUP":
|
|
12214
|
+
return classifyNode(where.expr, resolveField2);
|
|
12215
|
+
case "NOT": {
|
|
12216
|
+
const inner = classifyNode(where.expr, resolveField2);
|
|
12217
|
+
if (inner.capability !== "SUPERSET_PREFILTER") return inner;
|
|
12218
|
+
if (!hasRelativeDateReason(inner.reasons)) {
|
|
12219
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12133
12220
|
}
|
|
12134
|
-
return
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
}
|
|
12144
|
-
while (true) {
|
|
12145
|
-
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12146
|
-
return allRecords.slice(0, stopAfter);
|
|
12147
|
-
}
|
|
12148
|
-
if (allRecords.length >= maxRecords) {
|
|
12149
|
-
if (onLimit === "truncate") {
|
|
12150
|
-
if (!notified && options.onTruncate) {
|
|
12151
|
-
options.onTruncate(maxRecords);
|
|
12152
|
-
notified = true;
|
|
12153
|
-
}
|
|
12154
|
-
return allRecords.slice(0, maxRecords);
|
|
12155
|
-
}
|
|
12156
|
-
throw new FetchAllLimitError(limitMessage);
|
|
12157
|
-
}
|
|
12158
|
-
const remaining = fetchCap - allRecords.length;
|
|
12159
|
-
const maxPagesByLimit = Math.ceil(remaining / pageSize);
|
|
12160
|
-
const batchParallel = Math.max(1, Math.min(parallel, maxPagesByLimit));
|
|
12161
|
-
const batchOffsets = [];
|
|
12162
|
-
for (let i = 0; i < batchParallel; i++) {
|
|
12163
|
-
const off = windowOffset + i * pageSize;
|
|
12164
|
-
if (off >= KINTONE_MAX_OFFSET) break;
|
|
12165
|
-
batchOffsets.push(off);
|
|
12166
|
-
}
|
|
12167
|
-
const cq = buildCursorQuery(query, cursorId);
|
|
12168
|
-
const responses = await Promise.all(
|
|
12169
|
-
batchOffsets.map(
|
|
12170
|
-
(offset) => fetchPage(fetcher, app, cq, fetchFields, pageSize, offset)
|
|
12171
|
-
)
|
|
12172
|
-
);
|
|
12173
|
-
for (const response of responses) notifySearchAborted(response, options);
|
|
12174
|
-
let done = false;
|
|
12175
|
-
for (const res of responses) {
|
|
12176
|
-
allRecords.push(...res.records);
|
|
12177
|
-
if (stopAfter !== void 0 && allRecords.length >= stopAfter) {
|
|
12178
|
-
return allRecords.slice(0, stopAfter);
|
|
12179
|
-
}
|
|
12180
|
-
if (allRecords.length > maxRecords) {
|
|
12181
|
-
if (onLimit === "truncate") {
|
|
12182
|
-
if (!notified && options.onTruncate) {
|
|
12183
|
-
options.onTruncate(maxRecords);
|
|
12184
|
-
notified = true;
|
|
12185
|
-
}
|
|
12186
|
-
return allRecords.slice(0, maxRecords);
|
|
12187
|
-
}
|
|
12188
|
-
throw new FetchAllLimitError(limitMessage);
|
|
12189
|
-
}
|
|
12190
|
-
if (res.records.length < pageSize) {
|
|
12191
|
-
done = true;
|
|
12192
|
-
break;
|
|
12193
|
-
}
|
|
12194
|
-
}
|
|
12195
|
-
if (done) break;
|
|
12196
|
-
windowOffset += pageSize * batchOffsets.length;
|
|
12197
|
-
if (windowOffset >= KINTONE_MAX_OFFSET) {
|
|
12198
|
-
const lastRes = responses[responses.length - 1];
|
|
12199
|
-
cursorId = getLastId(lastRes.records);
|
|
12200
|
-
windowOffset = 0;
|
|
12201
|
-
}
|
|
12202
|
-
}
|
|
12203
|
-
return allRecords;
|
|
12204
|
-
}
|
|
12205
|
-
function notifySearchAborted(response, options) {
|
|
12206
|
-
if (response.searchAborted) options.onSearchAborted?.();
|
|
12207
|
-
}
|
|
12208
|
-
function extractIds(records) {
|
|
12209
|
-
return records.map((r) => {
|
|
12210
|
-
const raw = r["$id"]?.value;
|
|
12211
|
-
if (raw === void 0) {
|
|
12212
|
-
throw new Error(
|
|
12213
|
-
'\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'
|
|
12214
|
-
);
|
|
12215
|
-
}
|
|
12216
|
-
const id = Number(raw);
|
|
12217
|
-
if (!Number.isFinite(id)) {
|
|
12218
|
-
throw new Error(`$id \u306E\u5024\u304C\u6570\u5024\u3067\u306F\u3042\u308A\u307E\u305B\u3093: ${raw}`);
|
|
12219
|
-
}
|
|
12220
|
-
return id;
|
|
12221
|
-
});
|
|
12222
|
-
}
|
|
12223
|
-
var PAGE_SIZE_DEFAULT = 500;
|
|
12224
|
-
var PARALLEL_DEFAULT = 1;
|
|
12225
|
-
var MAX_RECORDS_DEFAULT = 1e4;
|
|
12226
|
-
var KINTONE_MAX_OFFSET = 1e4;
|
|
12227
|
-
async function fetchPage(fetcher, app, query, fields, pageSize, offset) {
|
|
12228
|
-
const pageQuery = buildPageQuery(query, pageSize, offset);
|
|
12229
|
-
return fetcher({ app, query: pageQuery, fields });
|
|
12230
|
-
}
|
|
12231
|
-
function buildCursorQuery(baseQuery, cursorId) {
|
|
12232
|
-
const base = baseQuery.trimEnd();
|
|
12233
|
-
if (cursorId <= 0) {
|
|
12234
|
-
return base ? `${base} order by $id asc` : "order by $id asc";
|
|
12235
|
-
}
|
|
12236
|
-
const cursor = `$id > ${cursorId} order by $id asc`;
|
|
12237
|
-
return base ? `(${base}) and ${cursor}` : cursor;
|
|
12238
|
-
}
|
|
12239
|
-
function buildPageQuery(query, pageSize, offset) {
|
|
12240
|
-
const base = query.trimEnd();
|
|
12241
|
-
const suffix = `limit ${pageSize} offset ${offset}`;
|
|
12242
|
-
return base ? `${base} ${suffix}` : suffix;
|
|
12243
|
-
}
|
|
12244
|
-
function getLastId(records) {
|
|
12245
|
-
const last = records[records.length - 1];
|
|
12246
|
-
const raw = last?.["$id"]?.value;
|
|
12247
|
-
if (raw === void 0) return 0;
|
|
12248
|
-
const id = Number(raw);
|
|
12249
|
-
return Number.isFinite(id) ? id : 0;
|
|
12250
|
-
}
|
|
12251
|
-
var FetchAllLimitError = class extends Error {
|
|
12252
|
-
constructor(message, completeInputWrapped = false) {
|
|
12253
|
-
super(message);
|
|
12254
|
-
this.completeInputWrapped = completeInputWrapped;
|
|
12255
|
-
this.name = "FetchAllLimitError";
|
|
12256
|
-
}
|
|
12257
|
-
};
|
|
12258
|
-
|
|
12259
|
-
// src/core/optimization/sharedPlanner.ts
|
|
12260
|
-
async function fetchRecordsForSharedPlan(getRecords, app, query, fields, options) {
|
|
12261
|
-
const records = await fetchAll(getRecords, app, query, fields, {
|
|
12262
|
-
maxRecords: options.maxRecords,
|
|
12263
|
-
parallel: options.parallel,
|
|
12264
|
-
onLimit: options.onLimit ?? "error",
|
|
12265
|
-
onTruncate: options.onTruncate,
|
|
12266
|
-
onSearchAborted: options.onSearchAborted
|
|
12267
|
-
});
|
|
12268
|
-
return {
|
|
12269
|
-
records,
|
|
12270
|
-
metrics: { fetchedRows: records.length }
|
|
12271
|
-
};
|
|
12272
|
-
}
|
|
12273
|
-
async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
12274
|
-
const { records, metrics } = await fetchRecordsForSharedPlan(
|
|
12275
|
-
getRecords,
|
|
12276
|
-
app,
|
|
12277
|
-
query,
|
|
12278
|
-
["$id"],
|
|
12279
|
-
options
|
|
12280
|
-
);
|
|
12281
|
-
return {
|
|
12282
|
-
ids: extractIds(records),
|
|
12283
|
-
metrics
|
|
12284
|
-
};
|
|
12285
|
-
}
|
|
12286
|
-
|
|
12287
|
-
// src/core/optimization/whereCapability.ts
|
|
12288
|
-
var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
|
|
12289
|
-
var RELATIVE_DATE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
12290
|
-
"DATE",
|
|
12291
|
-
"DATETIME",
|
|
12292
|
-
"CREATED_TIME",
|
|
12293
|
-
"UPDATED_TIME"
|
|
12294
|
-
]);
|
|
12295
|
-
var RELATIVE_DATE_OPERATORS = new Set(RANGE_AND_EQUALITY);
|
|
12296
|
-
var LEGACY_KINTONE_FUNCTION_FIELD_TYPES = /* @__PURE__ */ new Map([
|
|
12297
|
-
["TODAY", /* @__PURE__ */ new Set(["DATE", "DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
12298
|
-
["NOW", /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
12299
|
-
["LOGINUSER", /* @__PURE__ */ new Set(["CREATOR", "MODIFIER", "USER_SELECT"])],
|
|
12300
|
-
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["ORGANIZATION_SELECT"])]
|
|
12301
|
-
]);
|
|
12302
|
-
var LEGACY_KINTONE_FUNCTION_OPERATORS = /* @__PURE__ */ new Map([
|
|
12303
|
-
["TODAY", new Set(RANGE_AND_EQUALITY)],
|
|
12304
|
-
["NOW", new Set(RANGE_AND_EQUALITY)],
|
|
12305
|
-
["LOGINUSER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12306
|
-
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12307
|
-
]);
|
|
12308
|
-
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
12309
|
-
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
12310
|
-
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12311
|
-
["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12312
|
-
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12313
|
-
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12314
|
-
["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12315
|
-
["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12316
|
-
["DATE", new Set(RANGE_AND_EQUALITY)],
|
|
12317
|
-
["TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12318
|
-
["DATETIME", new Set(RANGE_AND_EQUALITY)],
|
|
12319
|
-
["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
12320
|
-
["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
12321
|
-
["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12322
|
-
["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12323
|
-
["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12324
|
-
["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12325
|
-
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12326
|
-
["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12327
|
-
["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12328
|
-
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12329
|
-
["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12330
|
-
["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12331
|
-
["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12332
|
-
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12333
|
-
["STATUS", new Set(EQUALITY_IN)]
|
|
12334
|
-
]);
|
|
12335
|
-
var LOCAL_VALID_OPERATORS = /* @__PURE__ */ new Map([
|
|
12336
|
-
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12337
|
-
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12338
|
-
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12339
|
-
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12340
|
-
]);
|
|
12341
|
-
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
12342
|
-
"RECORD_NUMBER",
|
|
12343
|
-
"__ID__",
|
|
12344
|
-
"CREATOR",
|
|
12345
|
-
"MODIFIER",
|
|
12346
|
-
"CREATED_TIME",
|
|
12347
|
-
"UPDATED_TIME",
|
|
12348
|
-
"DATE",
|
|
12349
|
-
"TIME",
|
|
12350
|
-
"DATETIME",
|
|
12351
|
-
"SINGLE_LINE_TEXT",
|
|
12352
|
-
"LINK",
|
|
12353
|
-
"NUMBER",
|
|
12354
|
-
"CALC",
|
|
12355
|
-
"MULTI_LINE_TEXT",
|
|
12356
|
-
"RICH_TEXT",
|
|
12357
|
-
"RADIO_BUTTON",
|
|
12358
|
-
"DROP_DOWN",
|
|
12359
|
-
"STATUS",
|
|
12360
|
-
// 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
|
|
12361
|
-
"KSQL_STRING",
|
|
12362
|
-
"KSQL_NUMBER",
|
|
12363
|
-
"KSQL_BOOLEAN"
|
|
12364
|
-
]);
|
|
12365
|
-
var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
12366
|
-
"CHECK_BOX",
|
|
12367
|
-
"MULTI_SELECT",
|
|
12368
|
-
"FILE",
|
|
12369
|
-
"USER_SELECT",
|
|
12370
|
-
"ORGANIZATION_SELECT",
|
|
12371
|
-
"GROUP_SELECT",
|
|
12372
|
-
"STATUS_ASSIGNEE",
|
|
12373
|
-
"CATEGORY"
|
|
12374
|
-
]);
|
|
12375
|
-
function nativeWhereOperatorsForType(fieldType) {
|
|
12376
|
-
return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
|
|
12377
|
-
}
|
|
12378
|
-
function normalizeChoiceEquality(where, resolveField2) {
|
|
12379
|
-
const rewrites = [];
|
|
12380
|
-
const visit = (node) => {
|
|
12381
|
-
if (node.type === "BINARY") {
|
|
12382
|
-
if ((node.op === "=" || node.op === "!=" || node.op === "<>") && node.left.type === "FIELD" && node.right.type === "STRING" && node.right.value !== "") {
|
|
12383
|
-
const semantics = resolveField2(node.left);
|
|
12384
|
-
if (semantics !== void 0 && semantics.compareMode === "option" && LOCAL_SCALAR_TYPES.has(semantics.fieldType) && nativeWhereOperatorsForType(semantics.fieldType).has("in") && semantics.optionOrder?.has(node.right.value) === true) {
|
|
12385
|
-
const normalizedOperator = node.op === "=" ? "IN" : "NOT_IN";
|
|
12386
|
-
rewrites.push({
|
|
12387
|
-
field: node.left,
|
|
12388
|
-
originalOperator: node.op,
|
|
12389
|
-
normalizedOperator,
|
|
12390
|
-
value: node.right.value
|
|
12391
|
-
});
|
|
12392
|
-
return {
|
|
12393
|
-
...node,
|
|
12394
|
-
op: normalizedOperator,
|
|
12395
|
-
right: { type: "IN_LIST", values: [node.right] }
|
|
12396
|
-
};
|
|
12397
|
-
}
|
|
12398
|
-
}
|
|
12399
|
-
return node;
|
|
12400
|
-
}
|
|
12401
|
-
if (node.type === "LOGICAL") {
|
|
12402
|
-
const left = visit(node.left);
|
|
12403
|
-
const right = visit(node.right);
|
|
12404
|
-
return left === node.left && right === node.right ? node : { ...node, left, right };
|
|
12405
|
-
}
|
|
12406
|
-
if (node.type === "GROUP" || node.type === "NOT") {
|
|
12407
|
-
const expr = visit(node.expr);
|
|
12408
|
-
return expr === node.expr ? node : { ...node, expr };
|
|
12409
|
-
}
|
|
12410
|
-
return node;
|
|
12411
|
-
};
|
|
12412
|
-
return { normalizedWhere: visit(where), rewrites };
|
|
12413
|
-
}
|
|
12414
|
-
function classifyWhereCapability(where, resolveField2) {
|
|
12415
|
-
if (where === null) {
|
|
12416
|
-
return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
|
|
12417
|
-
}
|
|
12418
|
-
return classifyNode(where, resolveField2);
|
|
12419
|
-
}
|
|
12420
|
-
function classifyNode(where, resolveField2) {
|
|
12421
|
-
switch (where.type) {
|
|
12422
|
-
case "BOOLEAN":
|
|
12423
|
-
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12424
|
-
case "BINARY":
|
|
12425
|
-
return classifyBinary(where.op, where.left, where.right, resolveField2);
|
|
12426
|
-
case "NULL_CHECK":
|
|
12427
|
-
if (where.field.type !== "FIELD") return localExpression();
|
|
12428
|
-
return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
|
|
12429
|
-
case "EXISTS":
|
|
12430
|
-
return localExpression();
|
|
12431
|
-
case "GROUP":
|
|
12432
|
-
return classifyNode(where.expr, resolveField2);
|
|
12433
|
-
case "NOT": {
|
|
12434
|
-
const inner = classifyNode(where.expr, resolveField2);
|
|
12435
|
-
if (inner.capability !== "SUPERSET_PREFILTER") return inner;
|
|
12436
|
-
if (!hasRelativeDateReason(inner.reasons)) {
|
|
12437
|
-
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12438
|
-
}
|
|
12439
|
-
return requireExactFunctionPushdown({
|
|
12440
|
-
capability: "LOCAL_ONLY",
|
|
12441
|
-
reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }, ...inner.reasons]
|
|
12442
|
-
});
|
|
12443
|
-
}
|
|
12444
|
-
case "LOGICAL": {
|
|
12445
|
-
const left = classifyNode(where.left, resolveField2);
|
|
12446
|
-
const right = classifyNode(where.right, resolveField2);
|
|
12447
|
-
return combineLogical(where.op, left, right);
|
|
12221
|
+
return requireExactFunctionPushdown({
|
|
12222
|
+
capability: "LOCAL_ONLY",
|
|
12223
|
+
reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }, ...inner.reasons]
|
|
12224
|
+
});
|
|
12225
|
+
}
|
|
12226
|
+
case "LOGICAL": {
|
|
12227
|
+
const left = classifyNode(where.left, resolveField2);
|
|
12228
|
+
const right = classifyNode(where.right, resolveField2);
|
|
12229
|
+
return combineLogical(where.op, left, right);
|
|
12448
12230
|
}
|
|
12449
12231
|
}
|
|
12450
12232
|
}
|
|
@@ -12505,24 +12287,68 @@ function classifyBinary(op, left, right, resolveField2) {
|
|
|
12505
12287
|
};
|
|
12506
12288
|
}
|
|
12507
12289
|
return {
|
|
12508
|
-
capability: "LOCAL_ONLY",
|
|
12290
|
+
capability: "LOCAL_ONLY",
|
|
12291
|
+
reasons: [{
|
|
12292
|
+
code: "WHERE_RESIDUAL",
|
|
12293
|
+
field: left.field,
|
|
12294
|
+
fieldType: semantics.fieldType,
|
|
12295
|
+
operator: nativeOp
|
|
12296
|
+
}]
|
|
12297
|
+
};
|
|
12298
|
+
}
|
|
12299
|
+
function isLegacyKintoneFunction(value) {
|
|
12300
|
+
return value.type === "KINTONE_FUNC" && LEGACY_KINTONE_FUNCTION_NAMES.has(value.name);
|
|
12301
|
+
}
|
|
12302
|
+
function classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2) {
|
|
12303
|
+
const operator = normalizeOperator(op);
|
|
12304
|
+
const functionName = right.name;
|
|
12305
|
+
if (!LEGACY_KINTONE_FUNCTION_NAMES.has(functionName) || left.type !== "FIELD") {
|
|
12306
|
+
return legacyKintoneFunctionUnsupported(
|
|
12307
|
+
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
12308
|
+
functionName,
|
|
12309
|
+
void 0,
|
|
12310
|
+
void 0,
|
|
12311
|
+
operator
|
|
12312
|
+
);
|
|
12313
|
+
}
|
|
12314
|
+
const semantics = resolveField2(left);
|
|
12315
|
+
const validFieldTypes = LEGACY_KINTONE_FUNCTION_FIELD_TYPES.get(functionName);
|
|
12316
|
+
if (!semantics || !validFieldTypes.has(semantics.fieldType) || semantics.inSubtable || semantics.requiresCollectionOperators) {
|
|
12317
|
+
return legacyKintoneFunctionUnsupported(
|
|
12318
|
+
"WHERE_KINTONE_FUNCTION_FIELD_TYPE_UNSUPPORTED",
|
|
12319
|
+
functionName,
|
|
12320
|
+
left.field,
|
|
12321
|
+
semantics?.fieldType,
|
|
12322
|
+
operator
|
|
12323
|
+
);
|
|
12324
|
+
}
|
|
12325
|
+
const validOperators = LEGACY_KINTONE_FUNCTION_OPERATORS.get(functionName);
|
|
12326
|
+
if (!validOperators.has(operator)) {
|
|
12327
|
+
return legacyKintoneFunctionUnsupported(
|
|
12328
|
+
"WHERE_KINTONE_FUNCTION_OPERATOR_UNSUPPORTED",
|
|
12329
|
+
functionName,
|
|
12330
|
+
left.field,
|
|
12331
|
+
semantics.fieldType,
|
|
12332
|
+
operator
|
|
12333
|
+
);
|
|
12334
|
+
}
|
|
12335
|
+
return {
|
|
12336
|
+
capability: "EXACT_PUSHDOWN",
|
|
12509
12337
|
reasons: [{
|
|
12510
|
-
code: "
|
|
12338
|
+
code: "WHERE_EXACT",
|
|
12339
|
+
functionName,
|
|
12511
12340
|
field: left.field,
|
|
12512
12341
|
fieldType: semantics.fieldType,
|
|
12513
|
-
operator
|
|
12342
|
+
operator
|
|
12514
12343
|
}]
|
|
12515
12344
|
};
|
|
12516
12345
|
}
|
|
12517
|
-
function
|
|
12518
|
-
return value.type === "KINTONE_FUNC" && LEGACY_KINTONE_FUNCTION_NAMES.has(value.name);
|
|
12519
|
-
}
|
|
12520
|
-
function classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2) {
|
|
12346
|
+
function classifyRelativeDateBinary(op, left, right, resolveField2) {
|
|
12521
12347
|
const operator = normalizeOperator(op);
|
|
12522
12348
|
const functionName = right.name;
|
|
12523
|
-
if (
|
|
12524
|
-
return
|
|
12525
|
-
"
|
|
12349
|
+
if (left.type !== "FIELD") {
|
|
12350
|
+
return relativeDateUnsupported(
|
|
12351
|
+
"WHERE_RELATIVE_DATE_CONTEXT_UNSUPPORTED",
|
|
12526
12352
|
functionName,
|
|
12527
12353
|
void 0,
|
|
12528
12354
|
void 0,
|
|
@@ -12530,258 +12356,567 @@ function classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2) {
|
|
|
12530
12356
|
);
|
|
12531
12357
|
}
|
|
12532
12358
|
const semantics = resolveField2(left);
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12536
|
-
"WHERE_KINTONE_FUNCTION_FIELD_TYPE_UNSUPPORTED",
|
|
12359
|
+
if (!semantics) {
|
|
12360
|
+
return relativeDateUnsupported(
|
|
12361
|
+
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
12537
12362
|
functionName,
|
|
12538
12363
|
left.field,
|
|
12539
|
-
|
|
12364
|
+
void 0,
|
|
12365
|
+
operator
|
|
12366
|
+
);
|
|
12367
|
+
}
|
|
12368
|
+
if (!hasValidRelativeDateArguments(right)) {
|
|
12369
|
+
return relativeDateUnsupported(
|
|
12370
|
+
"WHERE_RELATIVE_DATE_ARGUMENT_INVALID",
|
|
12371
|
+
functionName,
|
|
12372
|
+
left.field,
|
|
12373
|
+
semantics.fieldType,
|
|
12374
|
+
operator
|
|
12375
|
+
);
|
|
12376
|
+
}
|
|
12377
|
+
if (!RELATIVE_DATE_OPERATORS.has(operator)) {
|
|
12378
|
+
return relativeDateUnsupported(
|
|
12379
|
+
"WHERE_RELATIVE_DATE_OPERATOR_UNSUPPORTED",
|
|
12380
|
+
functionName,
|
|
12381
|
+
left.field,
|
|
12382
|
+
semantics.fieldType,
|
|
12383
|
+
operator
|
|
12384
|
+
);
|
|
12385
|
+
}
|
|
12386
|
+
if (!RELATIVE_DATE_FIELD_TYPES.has(semantics.fieldType) || semantics.inSubtable || semantics.requiresCollectionOperators) {
|
|
12387
|
+
return relativeDateUnsupported(
|
|
12388
|
+
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
12389
|
+
functionName,
|
|
12390
|
+
left.field,
|
|
12391
|
+
semantics.fieldType,
|
|
12392
|
+
operator
|
|
12393
|
+
);
|
|
12394
|
+
}
|
|
12395
|
+
return {
|
|
12396
|
+
capability: "EXACT_PUSHDOWN",
|
|
12397
|
+
reasons: [{
|
|
12398
|
+
code: "WHERE_EXACT",
|
|
12399
|
+
functionName,
|
|
12400
|
+
field: left.field,
|
|
12401
|
+
fieldType: semantics.fieldType,
|
|
12402
|
+
operator
|
|
12403
|
+
}]
|
|
12404
|
+
};
|
|
12405
|
+
}
|
|
12406
|
+
function hasValidRelativeDateArguments(value) {
|
|
12407
|
+
if (!("args" in value) || !value.args) return false;
|
|
12408
|
+
switch (value.name) {
|
|
12409
|
+
case "YESTERDAY":
|
|
12410
|
+
case "TOMORROW":
|
|
12411
|
+
case "THIS_YEAR":
|
|
12412
|
+
case "LAST_YEAR":
|
|
12413
|
+
case "NEXT_YEAR":
|
|
12414
|
+
return value.args.kind === "NONE";
|
|
12415
|
+
case "FROM_TODAY":
|
|
12416
|
+
return value.args.kind === "FROM_TODAY" && Number.isSafeInteger(value.args.offset) && value.args.offsetText === String(value.args.offset === 0 ? 0 : value.args.offset) && (value.args.unit === "DAYS" || value.args.unit === "WEEKS" || value.args.unit === "MONTHS" || value.args.unit === "YEARS");
|
|
12417
|
+
case "THIS_WEEK":
|
|
12418
|
+
case "LAST_WEEK":
|
|
12419
|
+
case "NEXT_WEEK":
|
|
12420
|
+
return value.args.kind === "WEEK" && (value.args.weekday === null || value.args.weekday === "SUNDAY" || value.args.weekday === "MONDAY" || value.args.weekday === "TUESDAY" || value.args.weekday === "WEDNESDAY" || value.args.weekday === "THURSDAY" || value.args.weekday === "FRIDAY" || value.args.weekday === "SATURDAY");
|
|
12421
|
+
case "THIS_MONTH":
|
|
12422
|
+
case "LAST_MONTH":
|
|
12423
|
+
case "NEXT_MONTH":
|
|
12424
|
+
return value.args.kind === "MONTH" && (value.args.day === null || value.args.day === "LAST" || Number.isInteger(value.args.day) && value.args.day >= 1 && value.args.day <= 31);
|
|
12425
|
+
default:
|
|
12426
|
+
return false;
|
|
12427
|
+
}
|
|
12428
|
+
}
|
|
12429
|
+
function classifyLocalOnlyField(field, operator, resolveField2) {
|
|
12430
|
+
const semantics = resolveField2(field);
|
|
12431
|
+
if (!semantics) return unsupported2("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
|
|
12432
|
+
if (!isLocallyValidOperator(semantics.fieldType, operator)) {
|
|
12433
|
+
return unsupported2(
|
|
12434
|
+
"WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE",
|
|
12435
|
+
field.field,
|
|
12436
|
+
semantics.fieldType,
|
|
12540
12437
|
operator
|
|
12541
12438
|
);
|
|
12542
12439
|
}
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12440
|
+
if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
|
|
12441
|
+
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
|
|
12442
|
+
}
|
|
12443
|
+
return {
|
|
12444
|
+
capability: "LOCAL_ONLY",
|
|
12445
|
+
reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
|
|
12446
|
+
};
|
|
12447
|
+
}
|
|
12448
|
+
function isLocallyValidOperator(fieldType, operator) {
|
|
12449
|
+
const policy = LOCAL_VALID_OPERATORS.get(fieldType);
|
|
12450
|
+
return policy === void 0 || policy.has(operator);
|
|
12451
|
+
}
|
|
12452
|
+
function hasLocalContract(fieldType, op) {
|
|
12453
|
+
if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
|
|
12454
|
+
if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
|
|
12455
|
+
return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
|
|
12456
|
+
}
|
|
12457
|
+
function normalizeOperator(op) {
|
|
12458
|
+
switch (op) {
|
|
12459
|
+
case "<>":
|
|
12460
|
+
return "!=";
|
|
12461
|
+
case "IN":
|
|
12462
|
+
return "in";
|
|
12463
|
+
case "NOT_IN":
|
|
12464
|
+
return "not in";
|
|
12465
|
+
case "LIKE":
|
|
12466
|
+
case "KLIKE":
|
|
12467
|
+
return "like";
|
|
12468
|
+
case "NOT_LIKE":
|
|
12469
|
+
case "NOT_KLIKE":
|
|
12470
|
+
return "not like";
|
|
12471
|
+
default:
|
|
12472
|
+
return op;
|
|
12473
|
+
}
|
|
12474
|
+
}
|
|
12475
|
+
function combineLogical(op, left, right) {
|
|
12476
|
+
const reasons = [...left.reasons, ...right.reasons];
|
|
12477
|
+
if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
|
|
12478
|
+
return requireExactFunctionPushdown({ capability: "UNSUPPORTED", reasons });
|
|
12479
|
+
}
|
|
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" };
|
|
12552
12627
|
}
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
reasons: [{
|
|
12556
|
-
code: "WHERE_EXACT",
|
|
12557
|
-
functionName,
|
|
12558
|
-
field: left.field,
|
|
12559
|
-
fieldType: semantics.fieldType,
|
|
12560
|
-
operator
|
|
12561
|
-
}]
|
|
12562
|
-
};
|
|
12563
|
-
}
|
|
12564
|
-
function classifyRelativeDateBinary(op, left, right, resolveField2) {
|
|
12565
|
-
const operator = normalizeOperator(op);
|
|
12566
|
-
const functionName = right.name;
|
|
12567
|
-
if (left.type !== "FIELD") {
|
|
12568
|
-
return relativeDateUnsupported(
|
|
12569
|
-
"WHERE_RELATIVE_DATE_CONTEXT_UNSUPPORTED",
|
|
12570
|
-
functionName,
|
|
12571
|
-
void 0,
|
|
12572
|
-
void 0,
|
|
12573
|
-
operator
|
|
12574
|
-
);
|
|
12628
|
+
if (!operators.has(">=") || !operators.has("<=")) {
|
|
12629
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_OPERATOR_UNAVAILABLE" };
|
|
12575
12630
|
}
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
return relativeDateUnsupported(
|
|
12579
|
-
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
12580
|
-
functionName,
|
|
12581
|
-
left.field,
|
|
12582
|
-
void 0,
|
|
12583
|
-
operator
|
|
12584
|
-
);
|
|
12631
|
+
if (input.values === void 0) {
|
|
12632
|
+
return { kind: "RANGE_CANDIDATE", relation: "superset", reason: "JOIN_KEY_VALUES_RUNTIME" };
|
|
12585
12633
|
}
|
|
12586
|
-
if (!
|
|
12587
|
-
return
|
|
12588
|
-
"WHERE_RELATIVE_DATE_ARGUMENT_INVALID",
|
|
12589
|
-
functionName,
|
|
12590
|
-
left.field,
|
|
12591
|
-
semantics.fieldType,
|
|
12592
|
-
operator
|
|
12593
|
-
);
|
|
12634
|
+
if (!semanticsMatch(input.fieldType, input.sourceSemantics)) {
|
|
12635
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_SEMANTICS_UNRESOLVED" };
|
|
12594
12636
|
}
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
functionName,
|
|
12599
|
-
left.field,
|
|
12600
|
-
semantics.fieldType,
|
|
12601
|
-
operator
|
|
12602
|
-
);
|
|
12637
|
+
const sourceSemantics = input.sourceSemantics;
|
|
12638
|
+
if (input.hasEmptyValue) {
|
|
12639
|
+
return { kind: "FALLBACK", reason: "JOIN_KEY_EMPTY_VALUE" };
|
|
12603
12640
|
}
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
operator
|
|
12611
|
-
);
|
|
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" };
|
|
12612
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;
|
|
12613
12662
|
return {
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
operator
|
|
12621
|
-
}]
|
|
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
|
|
12622
12669
|
};
|
|
12623
12670
|
}
|
|
12624
|
-
function
|
|
12625
|
-
if (
|
|
12626
|
-
switch (
|
|
12627
|
-
case "
|
|
12628
|
-
|
|
12629
|
-
case "
|
|
12630
|
-
|
|
12631
|
-
case "
|
|
12632
|
-
return
|
|
12633
|
-
case "
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
case "
|
|
12637
|
-
case "
|
|
12638
|
-
return value.args.kind === "WEEK" && (value.args.weekday === null || value.args.weekday === "SUNDAY" || value.args.weekday === "MONDAY" || value.args.weekday === "TUESDAY" || value.args.weekday === "WEDNESDAY" || value.args.weekday === "THURSDAY" || value.args.weekday === "FRIDAY" || value.args.weekday === "SATURDAY");
|
|
12639
|
-
case "THIS_MONTH":
|
|
12640
|
-
case "LAST_MONTH":
|
|
12641
|
-
case "NEXT_MONTH":
|
|
12642
|
-
return value.args.kind === "MONTH" && (value.args.day === null || value.args.day === "LAST" || Number.isInteger(value.args.day) && value.args.day >= 1 && value.args.day <= 31);
|
|
12643
|
-
default:
|
|
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":
|
|
12644
12685
|
return false;
|
|
12645
12686
|
}
|
|
12646
12687
|
}
|
|
12647
|
-
function
|
|
12648
|
-
|
|
12649
|
-
if (
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
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");
|
|
12657
12733
|
}
|
|
12658
|
-
|
|
12659
|
-
|
|
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;
|
|
12763
|
+
}
|
|
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
|
+
}
|
|
12660
12822
|
}
|
|
12661
|
-
return
|
|
12662
|
-
capability: "LOCAL_ONLY",
|
|
12663
|
-
reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
|
|
12664
|
-
};
|
|
12823
|
+
return allRecords;
|
|
12665
12824
|
}
|
|
12666
|
-
function
|
|
12667
|
-
|
|
12668
|
-
return policy === void 0 || policy.has(operator);
|
|
12825
|
+
function notifySearchAborted(response, options) {
|
|
12826
|
+
if (response.searchAborted) options.onSearchAborted?.();
|
|
12669
12827
|
}
|
|
12670
|
-
function
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
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;
|
|
12841
|
+
});
|
|
12674
12842
|
}
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
return "not in";
|
|
12683
|
-
case "LIKE":
|
|
12684
|
-
case "KLIKE":
|
|
12685
|
-
return "like";
|
|
12686
|
-
case "NOT_LIKE":
|
|
12687
|
-
case "NOT_KLIKE":
|
|
12688
|
-
return "not like";
|
|
12689
|
-
default:
|
|
12690
|
-
return op;
|
|
12691
|
-
}
|
|
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 });
|
|
12692
12850
|
}
|
|
12693
|
-
function
|
|
12694
|
-
const
|
|
12695
|
-
if (
|
|
12696
|
-
return
|
|
12697
|
-
}
|
|
12698
|
-
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
12699
|
-
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
12700
|
-
}
|
|
12701
|
-
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
12702
|
-
return requireExactFunctionPushdown({
|
|
12703
|
-
capability: "SUPERSET_PREFILTER",
|
|
12704
|
-
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
12705
|
-
});
|
|
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";
|
|
12706
12855
|
}
|
|
12707
|
-
|
|
12708
|
-
}
|
|
12709
|
-
function localExpression() {
|
|
12710
|
-
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12711
|
-
}
|
|
12712
|
-
function unsupported2(code, field, fieldType, operator) {
|
|
12713
|
-
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
12714
|
-
}
|
|
12715
|
-
function legacyKintoneFunctionUnsupported(code, functionName, field, fieldType, operator) {
|
|
12716
|
-
return requireExactFunctionPushdown({
|
|
12717
|
-
capability: "UNSUPPORTED",
|
|
12718
|
-
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12719
|
-
});
|
|
12720
|
-
}
|
|
12721
|
-
function relativeDateUnsupported(code, functionName, field, fieldType, operator) {
|
|
12722
|
-
return requireExactRelativeDatePushdown({
|
|
12723
|
-
capability: "UNSUPPORTED",
|
|
12724
|
-
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12725
|
-
});
|
|
12856
|
+
const cursor = `$id > ${cursorId} order by $id asc`;
|
|
12857
|
+
return base ? `(${base}) and ${cursor}` : cursor;
|
|
12726
12858
|
}
|
|
12727
|
-
function
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12859
|
+
function buildPageQuery(query, pageSize, offset) {
|
|
12860
|
+
const base = query.trimEnd();
|
|
12861
|
+
const suffix = `limit ${pageSize} offset ${offset}`;
|
|
12862
|
+
return base ? `${base} ${suffix}` : suffix;
|
|
12731
12863
|
}
|
|
12732
|
-
function
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
);
|
|
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;
|
|
12736
12870
|
}
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12871
|
+
var FetchAllLimitError = class extends Error {
|
|
12872
|
+
constructor(message, completeInputWrapped = false) {
|
|
12873
|
+
super(message);
|
|
12874
|
+
this.completeInputWrapped = completeInputWrapped;
|
|
12875
|
+
this.name = "FetchAllLimitError";
|
|
12740
12876
|
}
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
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
|
+
});
|
|
12744
12888
|
return {
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
...result.reasons,
|
|
12748
|
-
{
|
|
12749
|
-
code: "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN",
|
|
12750
|
-
functionName: relative.functionName,
|
|
12751
|
-
field: relative.field,
|
|
12752
|
-
fieldType: relative.fieldType,
|
|
12753
|
-
operator: relative.operator
|
|
12754
|
-
}
|
|
12755
|
-
]
|
|
12889
|
+
records,
|
|
12890
|
+
metrics: { fetchedRows: records.length }
|
|
12756
12891
|
};
|
|
12757
12892
|
}
|
|
12758
|
-
function
|
|
12759
|
-
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
(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
|
|
12766
12900
|
);
|
|
12767
12901
|
return {
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
...result.reasons,
|
|
12771
|
-
{
|
|
12772
|
-
code: "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN",
|
|
12773
|
-
functionName: legacy.functionName,
|
|
12774
|
-
field: legacy.field,
|
|
12775
|
-
fieldType: legacy.fieldType,
|
|
12776
|
-
operator: legacy.operator
|
|
12777
|
-
}
|
|
12778
|
-
]
|
|
12902
|
+
ids: extractIds(records),
|
|
12903
|
+
metrics
|
|
12779
12904
|
};
|
|
12780
12905
|
}
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
);
|
|
12906
|
+
|
|
12907
|
+
// src/core/optimization/joinNumberLiteralPolicy.ts
|
|
12908
|
+
function isJoinNumberLiteralSupported(literal) {
|
|
12909
|
+
const source = literal.raw ?? String(literal.value);
|
|
12910
|
+
const decimal = parseExactDecimal(source);
|
|
12911
|
+
if (decimal === null) return false;
|
|
12912
|
+
if (decimal.sign === 0) return numberLiteralText(literal) === "0";
|
|
12913
|
+
const fractionDigits = Math.max(decimal.scale, 0);
|
|
12914
|
+
const integerDigits = Math.max(decimal.coefficient.length - decimal.scale, 0);
|
|
12915
|
+
if (fractionDigits > 10 || integerDigits + fractionDigits > 30) {
|
|
12916
|
+
return false;
|
|
12917
|
+
}
|
|
12918
|
+
const canonical = formatPlainDecimal(decimal);
|
|
12919
|
+
return numberLiteralText(literal) === canonical;
|
|
12785
12920
|
}
|
|
12786
12921
|
|
|
12787
12922
|
// src/core/optimization/joinPredicatePushdown.ts
|
|
@@ -12792,6 +12927,14 @@ var SELECTION_TYPES = /* @__PURE__ */ new Set([
|
|
|
12792
12927
|
"MULTI_SELECT",
|
|
12793
12928
|
"STATUS"
|
|
12794
12929
|
]);
|
|
12930
|
+
var USER_CODE_TYPES = /* @__PURE__ */ new Set([
|
|
12931
|
+
"CREATOR",
|
|
12932
|
+
"MODIFIER",
|
|
12933
|
+
"USER_SELECT",
|
|
12934
|
+
"ORGANIZATION_SELECT",
|
|
12935
|
+
"GROUP_SELECT",
|
|
12936
|
+
"STATUS_ASSIGNEE"
|
|
12937
|
+
]);
|
|
12795
12938
|
var KLIKE_TYPES = /* @__PURE__ */ new Set([
|
|
12796
12939
|
"SINGLE_LINE_TEXT",
|
|
12797
12940
|
"LINK",
|
|
@@ -12799,7 +12942,7 @@ var KLIKE_TYPES = /* @__PURE__ */ new Set([
|
|
|
12799
12942
|
"RICH_TEXT",
|
|
12800
12943
|
"FILE"
|
|
12801
12944
|
]);
|
|
12802
|
-
var
|
|
12945
|
+
var DATETIME_TYPES2 = /* @__PURE__ */ new Set([
|
|
12803
12946
|
"DATETIME",
|
|
12804
12947
|
"CREATED_TIME",
|
|
12805
12948
|
"UPDATED_TIME"
|
|
@@ -13395,21 +13538,45 @@ function classifySupportedLeaf(predicate, owner, fieldType) {
|
|
|
13395
13538
|
return isPositiveSafeInteger(predicate.right) && (predicate.op === "=" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") ? "exact" : "unsafe";
|
|
13396
13539
|
}
|
|
13397
13540
|
if (fieldType === "RECORD_NUMBER") {
|
|
13398
|
-
return
|
|
13541
|
+
return classifySupersetScalarOrListLiteral(predicate);
|
|
13399
13542
|
}
|
|
13400
13543
|
if (fieldType === "NUMBER") {
|
|
13401
|
-
if (predicate.
|
|
13402
|
-
|
|
13403
|
-
|
|
13544
|
+
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST" && predicate.right.values.length > 0 && predicate.right.values.every(
|
|
13545
|
+
(value) => value.type === "NUMBER" && isJoinNumberLiteralSupported(value)
|
|
13546
|
+
)) {
|
|
13547
|
+
return "exact";
|
|
13548
|
+
}
|
|
13549
|
+
if ((predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") && predicate.right.type === "NUMBER" && isJoinNumberLiteralSupported(predicate.right)) {
|
|
13550
|
+
return "exact";
|
|
13551
|
+
}
|
|
13552
|
+
return "unsafe";
|
|
13553
|
+
}
|
|
13554
|
+
if (fieldType === "CALC") {
|
|
13555
|
+
return classifySupersetScalarOrListLiteral(predicate);
|
|
13556
|
+
}
|
|
13557
|
+
if (USER_CODE_TYPES.has(fieldType)) {
|
|
13558
|
+
if (predicate.op !== "IN" && predicate.op !== "NOT_IN" || predicate.right.type !== "IN_LIST" || predicate.right.values.length === 0) return "unsafe";
|
|
13559
|
+
return predicate.right.values.every(
|
|
13560
|
+
(value) => value.type === "STRING" && value.value !== ""
|
|
13561
|
+
) ? "exact" : "unsafe";
|
|
13404
13562
|
}
|
|
13405
|
-
if (fieldType === "SINGLE_LINE_TEXT") {
|
|
13406
|
-
|
|
13563
|
+
if (fieldType === "SINGLE_LINE_TEXT" || fieldType === "LINK") {
|
|
13564
|
+
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST" && predicate.right.values.length > 0 && predicate.right.values.every(
|
|
13565
|
+
(value) => value.type === "STRING" && value.value !== ""
|
|
13566
|
+
)) {
|
|
13567
|
+
return "exact";
|
|
13568
|
+
}
|
|
13569
|
+
return (predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>") && predicate.right.type === "STRING" && predicate.right.value !== "" ? "exact" : "unsafe";
|
|
13407
13570
|
}
|
|
13408
|
-
if (fieldType === "DATE" || fieldType === "TIME" ||
|
|
13409
|
-
if (predicate.op !== "=" || predicate.right.type !== "STRING") return "unsafe";
|
|
13410
|
-
if (fieldType === "DATE")
|
|
13411
|
-
|
|
13412
|
-
|
|
13571
|
+
if (fieldType === "DATE" || fieldType === "TIME" || DATETIME_TYPES2.has(fieldType)) {
|
|
13572
|
+
if (predicate.op !== "=" && predicate.op !== "!=" && predicate.op !== "<>" && predicate.op !== "<" && predicate.op !== ">" && predicate.op !== "<=" && predicate.op !== ">=" || predicate.right.type !== "STRING") return "unsafe";
|
|
13573
|
+
if (fieldType === "DATE") {
|
|
13574
|
+
return isCanonicalJoinDate(predicate.right.value) ? "exact" : "unsafe";
|
|
13575
|
+
}
|
|
13576
|
+
if (fieldType === "TIME") {
|
|
13577
|
+
return isCanonicalJoinTime(predicate.right.value) ? "exact" : "unsafe";
|
|
13578
|
+
}
|
|
13579
|
+
return isCanonicalJoinDateTime(predicate.right.value) ? "exact" : "unsafe";
|
|
13413
13580
|
}
|
|
13414
13581
|
if (SELECTION_TYPES.has(fieldType)) {
|
|
13415
13582
|
if (predicate.op !== "IN" && predicate.op !== "NOT_IN" || predicate.right.type !== "IN_LIST" || predicate.right.values.length === 0) return "unsafe";
|
|
@@ -13421,6 +13588,19 @@ function classifySupportedLeaf(predicate, owner, fieldType) {
|
|
|
13421
13588
|
}
|
|
13422
13589
|
return "unsafe";
|
|
13423
13590
|
}
|
|
13591
|
+
function classifySupersetScalarOrListLiteral(predicate) {
|
|
13592
|
+
const supportedLiteral = (value) => value.type === "NUMBER" && isJoinNumberLiteralSupported(value) || value.type === "STRING" && value.value !== "";
|
|
13593
|
+
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST") {
|
|
13594
|
+
const values = predicate.right.values;
|
|
13595
|
+
if (values.length > 0 && values.every(supportedLiteral) && values.every((value) => value.type === values[0].type)) {
|
|
13596
|
+
return "superset";
|
|
13597
|
+
}
|
|
13598
|
+
}
|
|
13599
|
+
if ((predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") && supportedLiteral(predicate.right)) {
|
|
13600
|
+
return "superset";
|
|
13601
|
+
}
|
|
13602
|
+
return "unsafe";
|
|
13603
|
+
}
|
|
13424
13604
|
function owned(source, fieldCode) {
|
|
13425
13605
|
return Object.freeze({
|
|
13426
13606
|
status: "OWNED",
|
|
@@ -13445,24 +13625,6 @@ function isPositiveSafeInteger(value) {
|
|
|
13445
13625
|
function isSafeIntegerLiteral(value) {
|
|
13446
13626
|
return /^-?\d+$/.test(numberLiteralText(value)) && Number.isSafeInteger(value.value);
|
|
13447
13627
|
}
|
|
13448
|
-
function isCanonicalDate(value) {
|
|
13449
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
13450
|
-
if (!match) return false;
|
|
13451
|
-
const year = Number(match[1]);
|
|
13452
|
-
const month = Number(match[2]);
|
|
13453
|
-
const day = Number(match[3]);
|
|
13454
|
-
const date = new Date(Date.UTC(year, month - 1, day));
|
|
13455
|
-
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
13456
|
-
}
|
|
13457
|
-
function isCanonicalTime(value) {
|
|
13458
|
-
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
13459
|
-
return match !== null && Number(match[1]) <= 23 && Number(match[2]) <= 59;
|
|
13460
|
-
}
|
|
13461
|
-
function isCanonicalDateTime(value) {
|
|
13462
|
-
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.exec(value);
|
|
13463
|
-
if (!match || !isCanonicalDate(match[1])) return false;
|
|
13464
|
-
return Number(match[2]) <= 23 && Number(match[3]) <= 59 && Number(match[4]) <= 59;
|
|
13465
|
-
}
|
|
13466
13628
|
function collectKlikes2(where, out) {
|
|
13467
13629
|
if (where === null) return;
|
|
13468
13630
|
if (isKlike(where)) {
|
|
@@ -20174,6 +20336,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
20174
20336
|
parallel,
|
|
20175
20337
|
options.onLimitReached ?? "error",
|
|
20176
20338
|
warnings,
|
|
20339
|
+
cacheContext,
|
|
20340
|
+
cteCache,
|
|
20177
20341
|
null,
|
|
20178
20342
|
plainGroupByPlan,
|
|
20179
20343
|
""
|
|
@@ -20562,6 +20726,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
20562
20726
|
parallel,
|
|
20563
20727
|
effectiveOptions.onLimitReached ?? "error",
|
|
20564
20728
|
warnings,
|
|
20729
|
+
cacheContext,
|
|
20730
|
+
cteCache,
|
|
20565
20731
|
pushDownCond,
|
|
20566
20732
|
plainGroupByPlan
|
|
20567
20733
|
);
|
|
@@ -20754,10 +20920,9 @@ function splitChunks(items, size) {
|
|
|
20754
20920
|
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
20755
20921
|
return out;
|
|
20756
20922
|
}
|
|
20757
|
-
var JOIN_IN_CHUNK_SIZE = 50;
|
|
20758
20923
|
var JOIN_IN_MAX_CHUNKS = 6;
|
|
20759
|
-
var JOIN_IN_MAX_KEYS =
|
|
20760
|
-
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 = "") {
|
|
20761
20926
|
if (join2.type !== "INNER") return null;
|
|
20762
20927
|
if (!join2.table.alias) return null;
|
|
20763
20928
|
if (join2.table.subtableCode) return null;
|
|
@@ -20780,17 +20945,47 @@ async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxR
|
|
|
20780
20945
|
}
|
|
20781
20946
|
const sourceRows2 = tables.get(sourceAlias);
|
|
20782
20947
|
if (!sourceRows2) return null;
|
|
20783
|
-
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;
|
|
20784
20960
|
for (const row of sourceRows2) {
|
|
20785
20961
|
const raw = row[sourceField]?.value;
|
|
20786
|
-
const txt = toScalarText(raw)
|
|
20787
|
-
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;
|
|
20788
20983
|
}
|
|
20789
|
-
|
|
20790
|
-
|
|
20791
|
-
if (
|
|
20984
|
+
if (keyPlan.kind === "RANGE_CANDIDATE") return null;
|
|
20985
|
+
const prefilterQueries = buildJoinKeyPrefilterQueries(keyPlan, joinField, sqlQuote);
|
|
20986
|
+
if (prefilterQueries.length === 0) {
|
|
20792
20987
|
warnings.add(
|
|
20793
|
-
`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`
|
|
20794
20989
|
);
|
|
20795
20990
|
return null;
|
|
20796
20991
|
}
|
|
@@ -20799,18 +20994,16 @@ async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxR
|
|
|
20799
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`);
|
|
20800
20995
|
markLimitReached(client, join2.table.appId);
|
|
20801
20996
|
};
|
|
20802
|
-
const chunks = splitChunks(values, JOIN_IN_CHUNK_SIZE);
|
|
20803
20997
|
const merged = [];
|
|
20804
20998
|
const seen = /* @__PURE__ */ new Set();
|
|
20805
|
-
for (const
|
|
20806
|
-
const inClause = `${joinField} in (${chunk3.map(sqlQuote).join(",")})`;
|
|
20999
|
+
for (const joinKeyQuery of prefilterQueries) {
|
|
20807
21000
|
const pushQueries = [
|
|
20808
21001
|
pushDownCond !== null ? whereToKintone(pushDownCond) : "",
|
|
20809
21002
|
additionalPushQuery
|
|
20810
21003
|
].filter((query2) => query2 !== "");
|
|
20811
21004
|
const query = pushQueries.reduce(
|
|
20812
21005
|
(combined, pushQuery) => `(${combined}) and (${pushQuery})`,
|
|
20813
|
-
|
|
21006
|
+
joinKeyQuery
|
|
20814
21007
|
);
|
|
20815
21008
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, join2.table.appId, query, fields, {
|
|
20816
21009
|
parallel,
|
|
@@ -24050,6 +24243,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
24050
24243
|
return cache;
|
|
24051
24244
|
}
|
|
24052
24245
|
var explainJoinPushdownPlans = /* @__PURE__ */ new WeakMap();
|
|
24246
|
+
var explainJoinKeyPrefilters = /* @__PURE__ */ new WeakMap();
|
|
24053
24247
|
var explainChoiceEqualityRewrites = /* @__PURE__ */ new WeakMap();
|
|
24054
24248
|
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
24055
24249
|
var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
|
|
@@ -24081,6 +24275,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24081
24275
|
(node) => node.source === source || JSON.stringify(node.source) === JSON.stringify(source)
|
|
24082
24276
|
);
|
|
24083
24277
|
const explainRelations = new Map(initialRelations ?? []);
|
|
24278
|
+
const staticExplainRelations = new Set(initialRelations?.keys() ?? []);
|
|
24084
24279
|
const explainSourceColumns = async (select) => {
|
|
24085
24280
|
const tables = [select.from, ...select.joins.map((join2) => join2.table)];
|
|
24086
24281
|
if (tables.length > 1 && select.columns.some(
|
|
@@ -24160,6 +24355,17 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24160
24355
|
const withStatement = node;
|
|
24161
24356
|
for (const cte of withStatement.ctes) {
|
|
24162
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
|
+
}
|
|
24163
24369
|
const columns = await inferExplainRelationColumns(cte.query);
|
|
24164
24370
|
explainRelations.set(cte.name, { rows: [], columns });
|
|
24165
24371
|
}
|
|
@@ -24240,6 +24446,71 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24240
24446
|
await loadTypedPushdownMeta(select, tracedClient, cacheContext)
|
|
24241
24447
|
);
|
|
24242
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);
|
|
24243
24514
|
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
24244
24515
|
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
24245
24516
|
if (select.orderMode !== "KINTONE_NATIVE") {
|
|
@@ -25356,9 +25627,13 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25356
25627
|
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
25357
25628
|
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
25358
25629
|
const joinBoundQuery = join2.table.alias ? runtimeJoinPlan?.queriesByAlias.get(join2.table.alias) : void 0;
|
|
25359
|
-
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(" | ");
|
|
25360
25635
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
25361
|
-
lines.push(` kintone query: ${
|
|
25636
|
+
for (const query of joinQueries) lines.push(` kintone query: ${query}`);
|
|
25362
25637
|
const joinPlanItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
25363
25638
|
(item) => item.targetAlias === join2.table.alias
|
|
25364
25639
|
);
|
|
@@ -25366,8 +25641,8 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25366
25641
|
(consumption) => consumption.targetAlias === join2.table.alias
|
|
25367
25642
|
);
|
|
25368
25643
|
if (emitFetch && join2.table.cteName === null) {
|
|
25369
|
-
const joinPending = !runtimeJoinPlan && joinCandidate !== null;
|
|
25370
|
-
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";
|
|
25371
25646
|
lines.push(renderFetchScope(createExplainFetchSource(
|
|
25372
25647
|
collector,
|
|
25373
25648
|
join2.table.appId,
|
|
@@ -25378,7 +25653,25 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25378
25653
|
joinPending
|
|
25379
25654
|
)));
|
|
25380
25655
|
}
|
|
25381
|
-
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) {
|
|
25382
25675
|
lines.push(` pushdown applied: ${joinBoundQuery}`);
|
|
25383
25676
|
lines.push(` relation: ${joinPlanItem?.relation ?? "exact"}`);
|
|
25384
25677
|
} else if (!runtimeJoinPlan && joinCandidate !== null) {
|