@rex0220/kintone-sql-tools 3.26.0 → 3.28.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 +714 -157
- package/dist-engine/index.cjs +10 -10
- package/dist-engine/index.mjs +10 -10
- package/dist-engine/ksql-engine.umd.js +10 -10
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +63 -25
- package/dist-engine/meta/esm.json +63 -25
- package/dist-engine/meta/umd.json +63 -25
- package/dist-mcp/ksql-mcp.js +719 -163
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -5957,6 +5957,133 @@ function collectKlikes(where, out) {
|
|
|
5957
5957
|
}
|
|
5958
5958
|
}
|
|
5959
5959
|
|
|
5960
|
+
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
5961
|
+
function buildRelativeDateFullScanExactPlan(input) {
|
|
5962
|
+
const {
|
|
5963
|
+
select,
|
|
5964
|
+
selectMode,
|
|
5965
|
+
capability,
|
|
5966
|
+
context,
|
|
5967
|
+
serializedWholeWhere,
|
|
5968
|
+
relativeFunctionNames
|
|
5969
|
+
} = input;
|
|
5970
|
+
if (select.where === null) return null;
|
|
5971
|
+
if (select.from.appId <= 0 || select.from.cteName !== null) return null;
|
|
5972
|
+
if (select.from.subtableCode) return null;
|
|
5973
|
+
if (select.joins.length > 0) return null;
|
|
5974
|
+
if (!context.allowFullScanExact) return null;
|
|
5975
|
+
if (select.orderMode === "KINTONE_NATIVE") return null;
|
|
5976
|
+
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
5977
|
+
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
5978
|
+
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
5979
|
+
const occurrences = serverOnlyFunctionOccurrencesInWhere(select.where);
|
|
5980
|
+
if (occurrences.length === 0) return null;
|
|
5981
|
+
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
5982
|
+
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
5983
|
+
return null;
|
|
5984
|
+
}
|
|
5985
|
+
const prefilterPlan = {
|
|
5986
|
+
prefilterWhere: select.where,
|
|
5987
|
+
residualWhere: null,
|
|
5988
|
+
exactRelativeLeaves: collectExactServerFunctionLeaves(select.where),
|
|
5989
|
+
relativeFunctionNames: new Set(occurrences),
|
|
5990
|
+
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
5991
|
+
capability: capability.capability,
|
|
5992
|
+
reasons: capability.reasons
|
|
5993
|
+
};
|
|
5994
|
+
const plan = {
|
|
5995
|
+
allowForm: "FULL_SCAN_EXACT",
|
|
5996
|
+
clientWhereEvaluation: false,
|
|
5997
|
+
serializedWholeWhere,
|
|
5998
|
+
prefilterPlan
|
|
5999
|
+
};
|
|
6000
|
+
assertRelativeDateFullScanExactPlan(plan, input, occurrences);
|
|
6001
|
+
return plan;
|
|
6002
|
+
}
|
|
6003
|
+
function assertRelativeDateFullScanExactPlan(plan, input, occurrences) {
|
|
6004
|
+
if (plan.allowForm !== "FULL_SCAN_EXACT") {
|
|
6005
|
+
throw new Error("FULL_SCAN_EXACT invariant: allowForm");
|
|
6006
|
+
}
|
|
6007
|
+
if (plan.clientWhereEvaluation !== false) {
|
|
6008
|
+
throw new Error("FULL_SCAN_EXACT invariant: clientWhereEvaluation");
|
|
6009
|
+
}
|
|
6010
|
+
if (input.capability.capability !== "EXACT_PUSHDOWN") {
|
|
6011
|
+
throw new Error("FULL_SCAN_EXACT invariant: capability");
|
|
6012
|
+
}
|
|
6013
|
+
if (input.select.where === null || plan.prefilterPlan.prefilterWhere !== input.select.where) {
|
|
6014
|
+
throw new Error("FULL_SCAN_EXACT invariant: whole WHERE identity");
|
|
6015
|
+
}
|
|
6016
|
+
if (plan.prefilterPlan.residualWhere !== null) {
|
|
6017
|
+
throw new Error("FULL_SCAN_EXACT invariant: residualWhere");
|
|
6018
|
+
}
|
|
6019
|
+
if (plan.prefilterPlan.capability !== "EXACT_PUSHDOWN") {
|
|
6020
|
+
throw new Error("FULL_SCAN_EXACT invariant: transport capability");
|
|
6021
|
+
}
|
|
6022
|
+
if (!serializedMultisetContains(plan.serializedWholeWhere, occurrences)) {
|
|
6023
|
+
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
6026
|
+
function serverOnlyFunctionOccurrencesInWhere(where) {
|
|
6027
|
+
const names = [];
|
|
6028
|
+
const visit = (node) => {
|
|
6029
|
+
if (Array.isArray(node)) {
|
|
6030
|
+
node.forEach(visit);
|
|
6031
|
+
return;
|
|
6032
|
+
}
|
|
6033
|
+
if (node === null || typeof node !== "object") return;
|
|
6034
|
+
const value = node;
|
|
6035
|
+
if (value["type"] === "SELECT") return;
|
|
6036
|
+
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isServerOnlyWhereFunctionName(value["name"])) {
|
|
6037
|
+
names.push(value["name"]);
|
|
6038
|
+
return;
|
|
6039
|
+
}
|
|
6040
|
+
Object.values(value).forEach(visit);
|
|
6041
|
+
};
|
|
6042
|
+
visit(where);
|
|
6043
|
+
return names;
|
|
6044
|
+
}
|
|
6045
|
+
function collectExactServerFunctionLeaves(where) {
|
|
6046
|
+
const leaves = [];
|
|
6047
|
+
const visit = (node) => {
|
|
6048
|
+
switch (node.type) {
|
|
6049
|
+
case "BINARY":
|
|
6050
|
+
if (node.right.type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.name) || node.right.type === "IN_LIST" && node.right.values.length === 1 && node.right.values[0].type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.values[0].name)) {
|
|
6051
|
+
leaves.push(node);
|
|
6052
|
+
}
|
|
6053
|
+
return;
|
|
6054
|
+
case "LOGICAL":
|
|
6055
|
+
visit(node.left);
|
|
6056
|
+
visit(node.right);
|
|
6057
|
+
return;
|
|
6058
|
+
case "NOT":
|
|
6059
|
+
case "GROUP":
|
|
6060
|
+
visit(node.expr);
|
|
6061
|
+
return;
|
|
6062
|
+
case "EXISTS":
|
|
6063
|
+
case "NULL_CHECK":
|
|
6064
|
+
case "BOOLEAN":
|
|
6065
|
+
return;
|
|
6066
|
+
}
|
|
6067
|
+
};
|
|
6068
|
+
visit(where);
|
|
6069
|
+
return leaves;
|
|
6070
|
+
}
|
|
6071
|
+
function sameOccurrenceList(actual, expected) {
|
|
6072
|
+
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
6073
|
+
}
|
|
6074
|
+
function serializedMultisetContains(query, expectedNames) {
|
|
6075
|
+
const expected = /* @__PURE__ */ new Map();
|
|
6076
|
+
for (const name of expectedNames) {
|
|
6077
|
+
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
6078
|
+
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
6079
|
+
}
|
|
6080
|
+
for (const [name, count] of expected) {
|
|
6081
|
+
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
6082
|
+
if ((matches?.length ?? 0) < count) return false;
|
|
6083
|
+
}
|
|
6084
|
+
return true;
|
|
6085
|
+
}
|
|
6086
|
+
|
|
5960
6087
|
// src/core/klikeValidation.ts
|
|
5961
6088
|
var KlikeValidationError = class extends Error {
|
|
5962
6089
|
constructor(message) {
|
|
@@ -6115,6 +6242,10 @@ function validateSelect(stmt) {
|
|
|
6115
6242
|
validateNestedSelects(stmt);
|
|
6116
6243
|
return;
|
|
6117
6244
|
}
|
|
6245
|
+
if (canDeferJoinWholeWhereKlikeValidation(stmt)) {
|
|
6246
|
+
validateNestedSelects(stmt);
|
|
6247
|
+
return;
|
|
6248
|
+
}
|
|
6118
6249
|
throw new KlikeValidationError(
|
|
6119
6250
|
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u306F\u3001\u7269\u7406\u30C6\u30FC\u30D6\u30EB\u306B\u5BFE\u3059\u308B AND \u30EA\u30FC\u30D5\u3068\u3057\u3066\u5FC5\u305A\u62BC\u3057\u4E0B\u3052\u3089\u308C\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6120
6251
|
);
|
|
@@ -6122,6 +6253,11 @@ function validateSelect(stmt) {
|
|
|
6122
6253
|
}
|
|
6123
6254
|
validateNestedSelects(stmt);
|
|
6124
6255
|
}
|
|
6256
|
+
function canDeferJoinWholeWhereKlikeValidation(stmt) {
|
|
6257
|
+
return stmt.where !== null && serverOnlyFunctionOccurrencesInWhere(stmt.where).length > 0 && stmt.joins.length > 0 && stmt.joins.every((join2) => join2.type === "INNER") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
|
|
6258
|
+
(table) => table.alias !== null && table.cteName === null && !table.subtableCode
|
|
6259
|
+
);
|
|
6260
|
+
}
|
|
6125
6261
|
function validateOwnKlikeExpressions(stmt) {
|
|
6126
6262
|
walkWithoutNestedSelects(stmt, (where) => {
|
|
6127
6263
|
if (!isKlike(where)) return;
|
|
@@ -10453,6 +10589,28 @@ function validateDeclaredBatchVariables(statements, input) {
|
|
|
10453
10589
|
return normalized;
|
|
10454
10590
|
}
|
|
10455
10591
|
|
|
10592
|
+
// src/core/outerJoinSearchAbortGuard.ts
|
|
10593
|
+
function isOuterJoinSelect(value) {
|
|
10594
|
+
if (value["type"] !== "SELECT" || !Array.isArray(value["joins"])) return false;
|
|
10595
|
+
return value["joins"].some((join2) => {
|
|
10596
|
+
if (join2 === null || typeof join2 !== "object") return false;
|
|
10597
|
+
const type = join2["type"];
|
|
10598
|
+
return type === "LEFT" || type === "RIGHT";
|
|
10599
|
+
});
|
|
10600
|
+
}
|
|
10601
|
+
function statementContainsOuterJoin(statement) {
|
|
10602
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10603
|
+
const visit = (value) => {
|
|
10604
|
+
if (Array.isArray(value)) return value.some(visit);
|
|
10605
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return false;
|
|
10606
|
+
seen.add(value);
|
|
10607
|
+
const object = value;
|
|
10608
|
+
if (isOuterJoinSelect(object)) return true;
|
|
10609
|
+
return Object.values(object).some(visit);
|
|
10610
|
+
};
|
|
10611
|
+
return visit(statement);
|
|
10612
|
+
}
|
|
10613
|
+
|
|
10456
10614
|
// src/core/explainMetadata.ts
|
|
10457
10615
|
function buildGroupingExplainMetadata(statement, canonicalItemCount) {
|
|
10458
10616
|
const grouping = normalizeGroupingSpec(statement);
|
|
@@ -11211,6 +11369,30 @@ function classifyJoinPushdownLeaf(predicate, sources) {
|
|
|
11211
11369
|
const relation = classifySupportedLeaf(predicate, owner, fieldType);
|
|
11212
11370
|
return relation === "unsafe" ? unsafe() : Object.freeze({ relation, owner });
|
|
11213
11371
|
}
|
|
11372
|
+
function classifyJoinServerFunctionLeaf(predicate, sources) {
|
|
11373
|
+
const functionOccurrences = serverOnlyFunctionOccurrencesInWhere(predicate);
|
|
11374
|
+
if (functionOccurrences.length === 0 || predicate.left.type !== "FIELD") {
|
|
11375
|
+
return Object.freeze({ relation: "unsafe" });
|
|
11376
|
+
}
|
|
11377
|
+
const owner = resolveJoinFieldOwner(predicate.left, sources);
|
|
11378
|
+
if (owner.status !== "OWNED" || owner.source.sourceKind !== "APP") {
|
|
11379
|
+
return Object.freeze({ relation: "unsafe" });
|
|
11380
|
+
}
|
|
11381
|
+
const capability = classifyWhereCapability(predicate, (field) => {
|
|
11382
|
+
const resolved = resolveJoinFieldOwner(field, sources);
|
|
11383
|
+
if (resolved.status !== "OWNED" || resolved.source !== owner.source) return void 0;
|
|
11384
|
+
const type = resolved.source.fieldTypes.get(resolved.fieldCode);
|
|
11385
|
+
return type === void 0 ? void 0 : resolveFieldSemantics({ fieldType: type });
|
|
11386
|
+
});
|
|
11387
|
+
if (capability.capability !== "EXACT_PUSHDOWN") {
|
|
11388
|
+
return Object.freeze({ relation: "unsafe" });
|
|
11389
|
+
}
|
|
11390
|
+
return Object.freeze({
|
|
11391
|
+
relation: "function-leaf-exact",
|
|
11392
|
+
owner,
|
|
11393
|
+
functionOccurrences: Object.freeze([...functionOccurrences])
|
|
11394
|
+
});
|
|
11395
|
+
}
|
|
11214
11396
|
function buildJoinPushdownPlan(where, sources) {
|
|
11215
11397
|
const allKlikes = /* @__PURE__ */ new Set();
|
|
11216
11398
|
collectKlikes2(where, allKlikes);
|
|
@@ -11221,15 +11403,370 @@ function buildJoinPushdownPlan(where, sources) {
|
|
|
11221
11403
|
predicate: fragment.predicate,
|
|
11222
11404
|
relation: fragment.relation
|
|
11223
11405
|
}));
|
|
11406
|
+
const serverFunctionFoundation = buildServerFunctionFoundation(where, sources);
|
|
11224
11407
|
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
11225
|
-
|
|
11408
|
+
if (serverFunctionFoundation.serverFunctionCandidate?.variant === "WHOLE_WHERE_EXACT") {
|
|
11409
|
+
for (const klike of allKlikes) appliedKlikes.add(klike);
|
|
11410
|
+
} else {
|
|
11411
|
+
for (const item of items) collectKlikes2(item.predicate, appliedKlikes);
|
|
11412
|
+
}
|
|
11226
11413
|
return Object.freeze({
|
|
11227
11414
|
items: Object.freeze(items),
|
|
11228
11415
|
appliedKlikes,
|
|
11229
11416
|
allKlikes: Object.freeze([...allKlikes]),
|
|
11230
|
-
rejections: Object.freeze(collectRejections(where, sources))
|
|
11417
|
+
rejections: Object.freeze(collectRejections(where, sources)),
|
|
11418
|
+
fetchQueriesByAlias: /* @__PURE__ */ new Map(),
|
|
11419
|
+
...serverFunctionFoundation
|
|
11420
|
+
});
|
|
11421
|
+
}
|
|
11422
|
+
function bindJoinServerFunctionFetches(plan, sources) {
|
|
11423
|
+
if (plan.serverFunctionCandidate?.staticContract !== "CONFIRMED" || plan.serverFunctionConsumptions.length === 0 || plan.residualServerFunctionOccurrences.length !== 0 || !sameStringMultiset(
|
|
11424
|
+
plan.allServerFunctionOccurrences,
|
|
11425
|
+
plan.adoptedServerFunctionOccurrences
|
|
11426
|
+
) || plan.allKlikes.some((klike) => !plan.appliedKlikes.has(klike))) {
|
|
11427
|
+
return plan;
|
|
11428
|
+
}
|
|
11429
|
+
const queryPartsByAlias = /* @__PURE__ */ new Map();
|
|
11430
|
+
const append = (alias, query) => {
|
|
11431
|
+
const parts = queryPartsByAlias.get(alias);
|
|
11432
|
+
if (parts) parts.push(query);
|
|
11433
|
+
else queryPartsByAlias.set(alias, [query]);
|
|
11434
|
+
};
|
|
11435
|
+
try {
|
|
11436
|
+
if (plan.serverFunctionCandidate.variant === "EXACT_LEAF") {
|
|
11437
|
+
for (const item of plan.items) {
|
|
11438
|
+
append(item.targetAlias, serializeJoinPushdownItem(item, sources));
|
|
11439
|
+
}
|
|
11440
|
+
}
|
|
11441
|
+
for (const consumption of plan.serverFunctionConsumptions) {
|
|
11442
|
+
const source = sources.find(
|
|
11443
|
+
(candidate) => candidate.alias === consumption.targetAlias && candidate.appId === consumption.appId && candidate.sourceKind === "APP"
|
|
11444
|
+
);
|
|
11445
|
+
if (!source) return plan;
|
|
11446
|
+
const serialized = serializeJoinPushdownItem({
|
|
11447
|
+
targetAlias: consumption.targetAlias,
|
|
11448
|
+
appId: consumption.appId,
|
|
11449
|
+
predicate: consumption.predicate,
|
|
11450
|
+
relation: "exact"
|
|
11451
|
+
}, sources);
|
|
11452
|
+
if (serialized !== consumption.serializedPredicate || !sameStringMultiset(
|
|
11453
|
+
serverFunctionOccurrencesInSerializedQuery(serialized),
|
|
11454
|
+
consumption.functionOccurrences
|
|
11455
|
+
)) {
|
|
11456
|
+
return plan;
|
|
11457
|
+
}
|
|
11458
|
+
append(consumption.targetAlias, serialized);
|
|
11459
|
+
}
|
|
11460
|
+
} catch {
|
|
11461
|
+
return plan;
|
|
11462
|
+
}
|
|
11463
|
+
const fetchQueriesByAlias = /* @__PURE__ */ new Map();
|
|
11464
|
+
for (const [alias, parts] of queryPartsByAlias) {
|
|
11465
|
+
fetchQueriesByAlias.set(
|
|
11466
|
+
alias,
|
|
11467
|
+
parts.length === 1 ? parts[0] : parts.map((part) => `(${part})`).join(" and ")
|
|
11468
|
+
);
|
|
11469
|
+
}
|
|
11470
|
+
const boundConsumptions = plan.serverFunctionConsumptions.map((consumption) => {
|
|
11471
|
+
const query = fetchQueriesByAlias.get(consumption.targetAlias);
|
|
11472
|
+
if (!query) return null;
|
|
11473
|
+
return Object.freeze({
|
|
11474
|
+
...consumption,
|
|
11475
|
+
fetchBinding: Object.freeze({
|
|
11476
|
+
status: "BOUND_TO_TARGET_FETCH",
|
|
11477
|
+
targetAlias: consumption.targetAlias,
|
|
11478
|
+
appId: consumption.appId,
|
|
11479
|
+
query
|
|
11480
|
+
})
|
|
11481
|
+
});
|
|
11482
|
+
});
|
|
11483
|
+
if (boundConsumptions.some((consumption) => consumption === null)) return plan;
|
|
11484
|
+
return Object.freeze({
|
|
11485
|
+
...plan,
|
|
11486
|
+
serverFunctionCandidate: Object.freeze({
|
|
11487
|
+
...plan.serverFunctionCandidate,
|
|
11488
|
+
fetchContract: "CONFIRMED"
|
|
11489
|
+
}),
|
|
11490
|
+
serverFunctionConsumptions: Object.freeze(
|
|
11491
|
+
boundConsumptions
|
|
11492
|
+
),
|
|
11493
|
+
fetchQueriesByAlias
|
|
11231
11494
|
});
|
|
11232
11495
|
}
|
|
11496
|
+
function isJoinServerFunctionFetchPlan(plan) {
|
|
11497
|
+
if (plan.serverFunctionCandidate === null || plan.serverFunctionCandidate.staticContract !== "CONFIRMED" || plan.serverFunctionCandidate.fetchContract !== "CONFIRMED" || plan.serverFunctionConsumptions.length === 0 || plan.residualServerFunctionOccurrences.length !== 0 || !sameStringMultiset(
|
|
11498
|
+
plan.allServerFunctionOccurrences,
|
|
11499
|
+
plan.adoptedServerFunctionOccurrences
|
|
11500
|
+
)) {
|
|
11501
|
+
return false;
|
|
11502
|
+
}
|
|
11503
|
+
if (!plan.serverFunctionConsumptions.every((consumption) => {
|
|
11504
|
+
const binding = consumption.fetchBinding;
|
|
11505
|
+
return binding !== "PENDING_STEP_2" && binding.status === "BOUND_TO_TARGET_FETCH" && binding.targetAlias === consumption.targetAlias && binding.appId === consumption.appId && binding.query === plan.fetchQueriesByAlias.get(consumption.targetAlias) && binding.query.includes(consumption.serializedPredicate);
|
|
11506
|
+
})) {
|
|
11507
|
+
return false;
|
|
11508
|
+
}
|
|
11509
|
+
for (const [alias, query] of plan.fetchQueriesByAlias) {
|
|
11510
|
+
const expected = plan.serverFunctionConsumptions.filter((consumption) => consumption.targetAlias === alias).flatMap((consumption) => [...consumption.functionOccurrences]);
|
|
11511
|
+
if (expected.length > 0 && !sameStringMultiset(
|
|
11512
|
+
serverFunctionOccurrencesInSerializedQuery(query),
|
|
11513
|
+
expected
|
|
11514
|
+
)) {
|
|
11515
|
+
return false;
|
|
11516
|
+
}
|
|
11517
|
+
}
|
|
11518
|
+
if (plan.serverFunctionCandidate.variant === "WHOLE_WHERE_EXACT" && (plan.serverFunctionConsumptions.length !== 1 || plan.serverFunctionConsumptions[0].consumption !== "whole-where" || plan.residualWhere !== null || plan.allKlikes.some((klike) => !plan.appliedKlikes.has(klike)) || countSerializedKlikes(queryForWholeWhere(plan)) !== plan.allKlikes.length)) {
|
|
11519
|
+
return false;
|
|
11520
|
+
}
|
|
11521
|
+
return true;
|
|
11522
|
+
}
|
|
11523
|
+
function buildServerFunctionFoundation(where, sources) {
|
|
11524
|
+
if (where === null) return emptyServerFunctionFoundation(null);
|
|
11525
|
+
const allOccurrences = Object.freeze([
|
|
11526
|
+
...serverOnlyFunctionOccurrencesInWhere(where)
|
|
11527
|
+
]);
|
|
11528
|
+
if (allOccurrences.length === 0) return emptyServerFunctionFoundation(where);
|
|
11529
|
+
const wholeWhere = buildWholeWhereServerFunctionFoundation(
|
|
11530
|
+
where,
|
|
11531
|
+
sources,
|
|
11532
|
+
allOccurrences
|
|
11533
|
+
);
|
|
11534
|
+
if (wholeWhere !== null) return wholeWhere;
|
|
11535
|
+
const candidates = collectServerFunctionLeavesOnAndSpine(where);
|
|
11536
|
+
const consumptions = [];
|
|
11537
|
+
const adoptedLeaves = /* @__PURE__ */ new Set();
|
|
11538
|
+
for (const leaf of candidates) {
|
|
11539
|
+
const classification = classifyJoinServerFunctionLeaf(leaf, sources);
|
|
11540
|
+
if (classification.relation !== "function-leaf-exact") continue;
|
|
11541
|
+
const guardItem = {
|
|
11542
|
+
targetAlias: classification.owner.alias,
|
|
11543
|
+
appId: classification.owner.appId,
|
|
11544
|
+
predicate: leaf,
|
|
11545
|
+
relation: "exact"
|
|
11546
|
+
};
|
|
11547
|
+
let serializedPredicate;
|
|
11548
|
+
try {
|
|
11549
|
+
serializedPredicate = serializeJoinPushdownItem(guardItem, sources);
|
|
11550
|
+
} catch {
|
|
11551
|
+
continue;
|
|
11552
|
+
}
|
|
11553
|
+
const serializedOccurrences = serverFunctionOccurrencesInSerializedQuery(serializedPredicate);
|
|
11554
|
+
if (!sameStringMultiset(serializedOccurrences, classification.functionOccurrences)) {
|
|
11555
|
+
continue;
|
|
11556
|
+
}
|
|
11557
|
+
adoptedLeaves.add(leaf);
|
|
11558
|
+
consumptions.push(Object.freeze({
|
|
11559
|
+
targetAlias: classification.owner.alias,
|
|
11560
|
+
appId: classification.owner.appId,
|
|
11561
|
+
predicate: leaf,
|
|
11562
|
+
functionLeaves: Object.freeze([leaf]),
|
|
11563
|
+
functionOccurrences: classification.functionOccurrences,
|
|
11564
|
+
relation: classification.relation,
|
|
11565
|
+
consumption: "leaf",
|
|
11566
|
+
serializedPredicate,
|
|
11567
|
+
staticProof: Object.freeze({
|
|
11568
|
+
classifier: "EXACT_PUSHDOWN",
|
|
11569
|
+
ownership: "OWNED",
|
|
11570
|
+
serialization: "OCCURRENCE_MULTISET_EXACT",
|
|
11571
|
+
residualIdentityConsumption: "CONFIRMED"
|
|
11572
|
+
}),
|
|
11573
|
+
fetchBinding: "PENDING_STEP_2"
|
|
11574
|
+
}));
|
|
11575
|
+
}
|
|
11576
|
+
const residualWhere = removeAdoptedLeavesFromAndSpine(where, adoptedLeaves);
|
|
11577
|
+
const residualOccurrences = Object.freeze(residualWhere === null ? [] : [...serverOnlyFunctionOccurrencesInWhere(residualWhere)]);
|
|
11578
|
+
const adoptedOccurrences = Object.freeze(consumptions.flatMap(
|
|
11579
|
+
(consumption) => [...consumption.functionOccurrences]
|
|
11580
|
+
));
|
|
11581
|
+
const staticContract = sameStringMultiset(allOccurrences, adoptedOccurrences) && residualOccurrences.length === 0 ? "CONFIRMED" : "INCOMPLETE";
|
|
11582
|
+
return {
|
|
11583
|
+
serverFunctionCandidate: Object.freeze({
|
|
11584
|
+
variant: "EXACT_LEAF",
|
|
11585
|
+
staticContract,
|
|
11586
|
+
fetchContract: "PENDING_STEP_2"
|
|
11587
|
+
}),
|
|
11588
|
+
serverFunctionConsumptions: Object.freeze(consumptions),
|
|
11589
|
+
allServerFunctionOccurrences: allOccurrences,
|
|
11590
|
+
adoptedServerFunctionOccurrences: adoptedOccurrences,
|
|
11591
|
+
residualWhere,
|
|
11592
|
+
residualServerFunctionOccurrences: residualOccurrences
|
|
11593
|
+
};
|
|
11594
|
+
}
|
|
11595
|
+
function buildWholeWhereServerFunctionFoundation(where, sources, allOccurrences) {
|
|
11596
|
+
const owner = classifyWholeWhereExactOwner(where, sources);
|
|
11597
|
+
if (owner === null) return null;
|
|
11598
|
+
const item = {
|
|
11599
|
+
targetAlias: owner.alias,
|
|
11600
|
+
appId: owner.appId,
|
|
11601
|
+
predicate: where,
|
|
11602
|
+
relation: "exact"
|
|
11603
|
+
};
|
|
11604
|
+
let serializedPredicate;
|
|
11605
|
+
try {
|
|
11606
|
+
serializedPredicate = serializeJoinPushdownItem(item, sources);
|
|
11607
|
+
} catch {
|
|
11608
|
+
return null;
|
|
11609
|
+
}
|
|
11610
|
+
if (!sameStringMultiset(
|
|
11611
|
+
serverFunctionOccurrencesInSerializedQuery(serializedPredicate),
|
|
11612
|
+
allOccurrences
|
|
11613
|
+
)) {
|
|
11614
|
+
return null;
|
|
11615
|
+
}
|
|
11616
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
11617
|
+
collectKlikes2(where, allKlikes);
|
|
11618
|
+
if (countSerializedKlikes(serializedPredicate) !== allKlikes.size) return null;
|
|
11619
|
+
const functionLeaves = Object.freeze(collectServerFunctionLeaves(where));
|
|
11620
|
+
const consumption = Object.freeze({
|
|
11621
|
+
targetAlias: owner.alias,
|
|
11622
|
+
appId: owner.appId,
|
|
11623
|
+
predicate: where,
|
|
11624
|
+
functionLeaves,
|
|
11625
|
+
functionOccurrences: Object.freeze([...allOccurrences]),
|
|
11626
|
+
relation: "function-leaf-exact",
|
|
11627
|
+
consumption: "whole-where",
|
|
11628
|
+
serializedPredicate,
|
|
11629
|
+
staticProof: Object.freeze({
|
|
11630
|
+
classifier: "EXACT_PUSHDOWN",
|
|
11631
|
+
ownership: "OWNED",
|
|
11632
|
+
serialization: "OCCURRENCE_MULTISET_EXACT",
|
|
11633
|
+
residualIdentityConsumption: "CONFIRMED"
|
|
11634
|
+
}),
|
|
11635
|
+
fetchBinding: "PENDING_STEP_2"
|
|
11636
|
+
});
|
|
11637
|
+
return {
|
|
11638
|
+
serverFunctionCandidate: Object.freeze({
|
|
11639
|
+
variant: "WHOLE_WHERE_EXACT",
|
|
11640
|
+
staticContract: "CONFIRMED",
|
|
11641
|
+
fetchContract: "PENDING_STEP_2"
|
|
11642
|
+
}),
|
|
11643
|
+
serverFunctionConsumptions: Object.freeze([consumption]),
|
|
11644
|
+
allServerFunctionOccurrences: Object.freeze([...allOccurrences]),
|
|
11645
|
+
adoptedServerFunctionOccurrences: Object.freeze([...allOccurrences]),
|
|
11646
|
+
residualWhere: null,
|
|
11647
|
+
residualServerFunctionOccurrences: Object.freeze([])
|
|
11648
|
+
};
|
|
11649
|
+
}
|
|
11650
|
+
function classifyWholeWhereExactOwner(where, sources) {
|
|
11651
|
+
switch (where.type) {
|
|
11652
|
+
case "BINARY": {
|
|
11653
|
+
const functionOccurrences = serverOnlyFunctionOccurrencesInWhere(where);
|
|
11654
|
+
if (functionOccurrences.length > 0) {
|
|
11655
|
+
const classification2 = classifyJoinServerFunctionLeaf(where, sources);
|
|
11656
|
+
return classification2.relation === "function-leaf-exact" ? classification2.owner : null;
|
|
11657
|
+
}
|
|
11658
|
+
const classification = classifyJoinPushdownLeaf(where, sources);
|
|
11659
|
+
return classification.relation === "exact" && classification.owner !== void 0 ? classification.owner : null;
|
|
11660
|
+
}
|
|
11661
|
+
case "LOGICAL": {
|
|
11662
|
+
const left = classifyWholeWhereExactOwner(where.left, sources);
|
|
11663
|
+
const right = classifyWholeWhereExactOwner(where.right, sources);
|
|
11664
|
+
return left !== null && right !== null && sameOwner(left, right) ? left : null;
|
|
11665
|
+
}
|
|
11666
|
+
case "GROUP":
|
|
11667
|
+
case "NOT":
|
|
11668
|
+
return classifyWholeWhereExactOwner(where.expr, sources);
|
|
11669
|
+
case "NULL_CHECK":
|
|
11670
|
+
case "EXISTS":
|
|
11671
|
+
case "BOOLEAN":
|
|
11672
|
+
return null;
|
|
11673
|
+
}
|
|
11674
|
+
}
|
|
11675
|
+
function collectServerFunctionLeaves(where) {
|
|
11676
|
+
switch (where.type) {
|
|
11677
|
+
case "BINARY":
|
|
11678
|
+
return serverOnlyFunctionOccurrencesInWhere(where).length > 0 ? [where] : [];
|
|
11679
|
+
case "LOGICAL":
|
|
11680
|
+
return [
|
|
11681
|
+
...collectServerFunctionLeaves(where.left),
|
|
11682
|
+
...collectServerFunctionLeaves(where.right)
|
|
11683
|
+
];
|
|
11684
|
+
case "GROUP":
|
|
11685
|
+
case "NOT":
|
|
11686
|
+
return collectServerFunctionLeaves(where.expr);
|
|
11687
|
+
case "NULL_CHECK":
|
|
11688
|
+
case "EXISTS":
|
|
11689
|
+
case "BOOLEAN":
|
|
11690
|
+
return [];
|
|
11691
|
+
}
|
|
11692
|
+
}
|
|
11693
|
+
function emptyServerFunctionFoundation(where) {
|
|
11694
|
+
const occurrences = where === null ? [] : serverOnlyFunctionOccurrencesInWhere(where);
|
|
11695
|
+
return {
|
|
11696
|
+
serverFunctionCandidate: null,
|
|
11697
|
+
serverFunctionConsumptions: Object.freeze([]),
|
|
11698
|
+
allServerFunctionOccurrences: Object.freeze([...occurrences]),
|
|
11699
|
+
adoptedServerFunctionOccurrences: Object.freeze([]),
|
|
11700
|
+
residualWhere: where,
|
|
11701
|
+
residualServerFunctionOccurrences: Object.freeze([...occurrences])
|
|
11702
|
+
};
|
|
11703
|
+
}
|
|
11704
|
+
function collectServerFunctionLeavesOnAndSpine(where) {
|
|
11705
|
+
switch (where.type) {
|
|
11706
|
+
case "BINARY":
|
|
11707
|
+
return serverOnlyFunctionOccurrencesInWhere(where).length > 0 ? [where] : [];
|
|
11708
|
+
case "LOGICAL":
|
|
11709
|
+
return where.op === "AND" ? [
|
|
11710
|
+
...collectServerFunctionLeavesOnAndSpine(where.left),
|
|
11711
|
+
...collectServerFunctionLeavesOnAndSpine(where.right)
|
|
11712
|
+
] : [];
|
|
11713
|
+
case "GROUP":
|
|
11714
|
+
return collectServerFunctionLeavesOnAndSpine(where.expr);
|
|
11715
|
+
case "NOT":
|
|
11716
|
+
case "NULL_CHECK":
|
|
11717
|
+
case "EXISTS":
|
|
11718
|
+
case "BOOLEAN":
|
|
11719
|
+
return [];
|
|
11720
|
+
}
|
|
11721
|
+
}
|
|
11722
|
+
function removeAdoptedLeavesFromAndSpine(where, adoptedLeaves) {
|
|
11723
|
+
if (where.type === "BINARY") return adoptedLeaves.has(where) ? null : where;
|
|
11724
|
+
if (where.type === "LOGICAL") {
|
|
11725
|
+
if (where.op !== "AND") return where;
|
|
11726
|
+
const left = removeAdoptedLeavesFromAndSpine(where.left, adoptedLeaves);
|
|
11727
|
+
const right = removeAdoptedLeavesFromAndSpine(where.right, adoptedLeaves);
|
|
11728
|
+
if (left === null) return right;
|
|
11729
|
+
if (right === null) return left;
|
|
11730
|
+
if (left === where.left && right === where.right) return where;
|
|
11731
|
+
return { ...where, left, right };
|
|
11732
|
+
}
|
|
11733
|
+
if (where.type === "GROUP") {
|
|
11734
|
+
const expr = removeAdoptedLeavesFromAndSpine(where.expr, adoptedLeaves);
|
|
11735
|
+
if (expr === null) return null;
|
|
11736
|
+
return expr === where.expr ? where : { ...where, expr };
|
|
11737
|
+
}
|
|
11738
|
+
return where;
|
|
11739
|
+
}
|
|
11740
|
+
function serverFunctionOccurrencesInSerializedQuery(query) {
|
|
11741
|
+
const matches = [];
|
|
11742
|
+
for (const name of SERVER_ONLY_WHERE_FUNCTION_NAMES) {
|
|
11743
|
+
const pattern = new RegExp(`\\b${name}\\s*\\(`, "g");
|
|
11744
|
+
let match;
|
|
11745
|
+
while ((match = pattern.exec(query)) !== null) {
|
|
11746
|
+
matches.push({ name, index: match.index });
|
|
11747
|
+
}
|
|
11748
|
+
}
|
|
11749
|
+
return matches.sort((left, right) => left.index - right.index).map((match) => match.name);
|
|
11750
|
+
}
|
|
11751
|
+
function sameStringMultiset(left, right) {
|
|
11752
|
+
if (left.length !== right.length) return false;
|
|
11753
|
+
const counts = /* @__PURE__ */ new Map();
|
|
11754
|
+
for (const value of left) counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
11755
|
+
for (const value of right) {
|
|
11756
|
+
const count = counts.get(value);
|
|
11757
|
+
if (count === void 0) return false;
|
|
11758
|
+
if (count === 1) counts.delete(value);
|
|
11759
|
+
else counts.set(value, count - 1);
|
|
11760
|
+
}
|
|
11761
|
+
return counts.size === 0;
|
|
11762
|
+
}
|
|
11763
|
+
function countSerializedKlikes(query) {
|
|
11764
|
+
return [...query.matchAll(/\s(?:not\s+)?like\s/g)].length;
|
|
11765
|
+
}
|
|
11766
|
+
function queryForWholeWhere(plan) {
|
|
11767
|
+
const consumption = plan.serverFunctionConsumptions[0];
|
|
11768
|
+
return consumption === void 0 ? "" : plan.fetchQueriesByAlias.get(consumption.targetAlias) ?? "";
|
|
11769
|
+
}
|
|
11233
11770
|
function collectRejections(where, sources) {
|
|
11234
11771
|
if (where === null) return [];
|
|
11235
11772
|
const reasons = /* @__PURE__ */ new Set();
|
|
@@ -11300,6 +11837,9 @@ function assertPredicateOwnership(predicate, item, sources) {
|
|
|
11300
11837
|
case "GROUP":
|
|
11301
11838
|
assertPredicateOwnership(predicate.expr, item, sources);
|
|
11302
11839
|
return;
|
|
11840
|
+
case "NOT":
|
|
11841
|
+
assertPredicateOwnership(predicate.expr, item, sources);
|
|
11842
|
+
return;
|
|
11303
11843
|
case "BINARY": {
|
|
11304
11844
|
if (predicate.left.type !== "FIELD" || containsFieldReference(predicate.right)) {
|
|
11305
11845
|
throw joinOwnershipError(item, "predicate contains a non-target or RHS field reference");
|
|
@@ -11310,7 +11850,6 @@ function assertPredicateOwnership(predicate, item, sources) {
|
|
|
11310
11850
|
}
|
|
11311
11851
|
return;
|
|
11312
11852
|
}
|
|
11313
|
-
case "NOT":
|
|
11314
11853
|
case "NULL_CHECK":
|
|
11315
11854
|
case "EXISTS":
|
|
11316
11855
|
case "BOOLEAN":
|
|
@@ -13116,133 +13655,6 @@ function deepClone(value, seen = /* @__PURE__ */ new Map()) {
|
|
|
13116
13655
|
return clone;
|
|
13117
13656
|
}
|
|
13118
13657
|
|
|
13119
|
-
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
13120
|
-
function buildRelativeDateFullScanExactPlan(input) {
|
|
13121
|
-
const {
|
|
13122
|
-
select,
|
|
13123
|
-
selectMode,
|
|
13124
|
-
capability,
|
|
13125
|
-
context,
|
|
13126
|
-
serializedWholeWhere,
|
|
13127
|
-
relativeFunctionNames
|
|
13128
|
-
} = input;
|
|
13129
|
-
if (select.where === null) return null;
|
|
13130
|
-
if (select.from.appId <= 0 || select.from.cteName !== null) return null;
|
|
13131
|
-
if (select.from.subtableCode) return null;
|
|
13132
|
-
if (select.joins.length > 0) return null;
|
|
13133
|
-
if (!context.allowFullScanExact) return null;
|
|
13134
|
-
if (select.orderMode === "KINTONE_NATIVE") return null;
|
|
13135
|
-
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
13136
|
-
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
13137
|
-
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
13138
|
-
const occurrences = serverOnlyFunctionOccurrencesInWhere(select.where);
|
|
13139
|
-
if (occurrences.length === 0) return null;
|
|
13140
|
-
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
13141
|
-
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
13142
|
-
return null;
|
|
13143
|
-
}
|
|
13144
|
-
const prefilterPlan = {
|
|
13145
|
-
prefilterWhere: select.where,
|
|
13146
|
-
residualWhere: null,
|
|
13147
|
-
exactRelativeLeaves: collectExactServerFunctionLeaves(select.where),
|
|
13148
|
-
relativeFunctionNames: new Set(occurrences),
|
|
13149
|
-
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
13150
|
-
capability: capability.capability,
|
|
13151
|
-
reasons: capability.reasons
|
|
13152
|
-
};
|
|
13153
|
-
const plan = {
|
|
13154
|
-
allowForm: "FULL_SCAN_EXACT",
|
|
13155
|
-
clientWhereEvaluation: false,
|
|
13156
|
-
serializedWholeWhere,
|
|
13157
|
-
prefilterPlan
|
|
13158
|
-
};
|
|
13159
|
-
assertRelativeDateFullScanExactPlan(plan, input, occurrences);
|
|
13160
|
-
return plan;
|
|
13161
|
-
}
|
|
13162
|
-
function assertRelativeDateFullScanExactPlan(plan, input, occurrences) {
|
|
13163
|
-
if (plan.allowForm !== "FULL_SCAN_EXACT") {
|
|
13164
|
-
throw new Error("FULL_SCAN_EXACT invariant: allowForm");
|
|
13165
|
-
}
|
|
13166
|
-
if (plan.clientWhereEvaluation !== false) {
|
|
13167
|
-
throw new Error("FULL_SCAN_EXACT invariant: clientWhereEvaluation");
|
|
13168
|
-
}
|
|
13169
|
-
if (input.capability.capability !== "EXACT_PUSHDOWN") {
|
|
13170
|
-
throw new Error("FULL_SCAN_EXACT invariant: capability");
|
|
13171
|
-
}
|
|
13172
|
-
if (input.select.where === null || plan.prefilterPlan.prefilterWhere !== input.select.where) {
|
|
13173
|
-
throw new Error("FULL_SCAN_EXACT invariant: whole WHERE identity");
|
|
13174
|
-
}
|
|
13175
|
-
if (plan.prefilterPlan.residualWhere !== null) {
|
|
13176
|
-
throw new Error("FULL_SCAN_EXACT invariant: residualWhere");
|
|
13177
|
-
}
|
|
13178
|
-
if (plan.prefilterPlan.capability !== "EXACT_PUSHDOWN") {
|
|
13179
|
-
throw new Error("FULL_SCAN_EXACT invariant: transport capability");
|
|
13180
|
-
}
|
|
13181
|
-
if (!serializedMultisetContains(plan.serializedWholeWhere, occurrences)) {
|
|
13182
|
-
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
13183
|
-
}
|
|
13184
|
-
}
|
|
13185
|
-
function serverOnlyFunctionOccurrencesInWhere(where) {
|
|
13186
|
-
const names = [];
|
|
13187
|
-
const visit = (node) => {
|
|
13188
|
-
if (Array.isArray(node)) {
|
|
13189
|
-
node.forEach(visit);
|
|
13190
|
-
return;
|
|
13191
|
-
}
|
|
13192
|
-
if (node === null || typeof node !== "object") return;
|
|
13193
|
-
const value = node;
|
|
13194
|
-
if (value["type"] === "SELECT") return;
|
|
13195
|
-
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isServerOnlyWhereFunctionName(value["name"])) {
|
|
13196
|
-
names.push(value["name"]);
|
|
13197
|
-
return;
|
|
13198
|
-
}
|
|
13199
|
-
Object.values(value).forEach(visit);
|
|
13200
|
-
};
|
|
13201
|
-
visit(where);
|
|
13202
|
-
return names;
|
|
13203
|
-
}
|
|
13204
|
-
function collectExactServerFunctionLeaves(where) {
|
|
13205
|
-
const leaves = [];
|
|
13206
|
-
const visit = (node) => {
|
|
13207
|
-
switch (node.type) {
|
|
13208
|
-
case "BINARY":
|
|
13209
|
-
if (node.right.type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.name) || node.right.type === "IN_LIST" && node.right.values.length === 1 && node.right.values[0].type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.values[0].name)) {
|
|
13210
|
-
leaves.push(node);
|
|
13211
|
-
}
|
|
13212
|
-
return;
|
|
13213
|
-
case "LOGICAL":
|
|
13214
|
-
visit(node.left);
|
|
13215
|
-
visit(node.right);
|
|
13216
|
-
return;
|
|
13217
|
-
case "NOT":
|
|
13218
|
-
case "GROUP":
|
|
13219
|
-
visit(node.expr);
|
|
13220
|
-
return;
|
|
13221
|
-
case "EXISTS":
|
|
13222
|
-
case "NULL_CHECK":
|
|
13223
|
-
case "BOOLEAN":
|
|
13224
|
-
return;
|
|
13225
|
-
}
|
|
13226
|
-
};
|
|
13227
|
-
visit(where);
|
|
13228
|
-
return leaves;
|
|
13229
|
-
}
|
|
13230
|
-
function sameOccurrenceList(actual, expected) {
|
|
13231
|
-
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
13232
|
-
}
|
|
13233
|
-
function serializedMultisetContains(query, expectedNames) {
|
|
13234
|
-
const expected = /* @__PURE__ */ new Map();
|
|
13235
|
-
for (const name of expectedNames) {
|
|
13236
|
-
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
13237
|
-
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
13238
|
-
}
|
|
13239
|
-
for (const [name, count] of expected) {
|
|
13240
|
-
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
13241
|
-
if ((matches?.length ?? 0) < count) return false;
|
|
13242
|
-
}
|
|
13243
|
-
return true;
|
|
13244
|
-
}
|
|
13245
|
-
|
|
13246
13658
|
// src/core/optimization/relativeDatePushdownGuard.ts
|
|
13247
13659
|
function relativeDateFunctionNamesInNode(node, stopAtNestedSelect) {
|
|
13248
13660
|
const names = [];
|
|
@@ -13463,6 +13875,11 @@ function serializationContainsFunctions(query, names) {
|
|
|
13463
13875
|
function allowRelativeDatePrefilterPlan(select, decomposition) {
|
|
13464
13876
|
return decomposition.eligible === true && resolveSelectMode(select) === "FULL_SCAN" && select.orderMode !== "KINTONE_NATIVE" && select.from.cteName === null && !select.from.subtableCode && select.joins.length === 0;
|
|
13465
13877
|
}
|
|
13878
|
+
function allowJoinServerFunctionPlan(select, plan) {
|
|
13879
|
+
return select.where !== null && select.joins.length > 0 && select.joins.every((join2) => join2.type === "INNER") && [select.from, ...select.joins.map((join2) => join2.table)].every(
|
|
13880
|
+
(table) => table.alias !== null && table.cteName === null && !table.subtableCode
|
|
13881
|
+
) && isJoinServerFunctionFetchPlan(plan);
|
|
13882
|
+
}
|
|
13466
13883
|
function rejectedNode(candidate) {
|
|
13467
13884
|
return {
|
|
13468
13885
|
kind: candidate.kind,
|
|
@@ -13562,6 +13979,14 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13562
13979
|
allowed2 = true;
|
|
13563
13980
|
}
|
|
13564
13981
|
}
|
|
13982
|
+
let joinServerFunctionPlan;
|
|
13983
|
+
if (!allowed2 && resolver.joinServerFunctionPlan) {
|
|
13984
|
+
const candidatePlan = await resolver.joinServerFunctionPlan(select);
|
|
13985
|
+
if (candidatePlan !== null && allowJoinServerFunctionPlan(select, candidatePlan)) {
|
|
13986
|
+
joinServerFunctionPlan = candidatePlan;
|
|
13987
|
+
allowed2 = true;
|
|
13988
|
+
}
|
|
13989
|
+
}
|
|
13565
13990
|
const node2 = {
|
|
13566
13991
|
kind: candidate.kind,
|
|
13567
13992
|
source: candidate.source,
|
|
@@ -13572,6 +13997,11 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13572
13997
|
restQuery: restQuery2,
|
|
13573
13998
|
...prefilterPlan ? { prefilterPlan, phase2PrefilterEligible } : {},
|
|
13574
13999
|
...fullScanExactPlan ? { fullScanExactPlan, allowForm: fullScanExactPlan.allowForm } : {},
|
|
14000
|
+
...joinServerFunctionPlan ? {
|
|
14001
|
+
joinServerFunctionPlan,
|
|
14002
|
+
allowForm: "JOIN_SERVER_FUNCTION_EXACT",
|
|
14003
|
+
joinServerFunctionVariant: joinServerFunctionPlan.serverFunctionCandidate.variant
|
|
14004
|
+
} : {},
|
|
13575
14005
|
clientWhereEvaluation: !allowed2,
|
|
13576
14006
|
allowed: allowed2
|
|
13577
14007
|
};
|
|
@@ -13663,7 +14093,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13663
14093
|
}
|
|
13664
14094
|
const occurrences = collectServerFunctionOccurrences(stmt.where);
|
|
13665
14095
|
if (occurrences.length === 0) return reject(["NO_RELATIVE_DATE"]);
|
|
13666
|
-
const spine =
|
|
14096
|
+
const spine = collectServerFunctionLeavesOnAndSpine2(stmt.where, resolveField2);
|
|
13667
14097
|
if (!spine.ok) return reject(spine.reasonCodes);
|
|
13668
14098
|
if (!sameRelativeMultiset(occurrences, spine.leaves)) {
|
|
13669
14099
|
return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
@@ -13741,7 +14171,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13741
14171
|
}
|
|
13742
14172
|
};
|
|
13743
14173
|
}
|
|
13744
|
-
function
|
|
14174
|
+
function collectServerFunctionLeavesOnAndSpine2(where, resolveField2) {
|
|
13745
14175
|
const leaves = [];
|
|
13746
14176
|
let failure = null;
|
|
13747
14177
|
const visit = (node) => {
|
|
@@ -14840,7 +15270,7 @@ async function execute(sql, client, options = {}) {
|
|
|
14840
15270
|
const guardedClient = wrapClientWithSearchAbort(
|
|
14841
15271
|
countedClient,
|
|
14842
15272
|
collector,
|
|
14843
|
-
!isSelectLikeStatement(stmt)
|
|
15273
|
+
!isSelectLikeStatement(stmt) || statementContainsOuterJoin(stmt)
|
|
14844
15274
|
);
|
|
14845
15275
|
const result = await executeParsedStatement(
|
|
14846
15276
|
stmt,
|
|
@@ -14959,9 +15389,14 @@ function wrapClientWithMetrics(client, metrics) {
|
|
|
14959
15389
|
}
|
|
14960
15390
|
};
|
|
14961
15391
|
}
|
|
15392
|
+
var SEARCH_ABORT_FAIL_CLOSED = /* @__PURE__ */ Symbol("searchAbortFailClosed");
|
|
14962
15393
|
function wrapClientWithSearchAbort(client, collector, failClosed) {
|
|
15394
|
+
if (failClosed && client[SEARCH_ABORT_FAIL_CLOSED]) {
|
|
15395
|
+
return client;
|
|
15396
|
+
}
|
|
14963
15397
|
return {
|
|
14964
15398
|
...client,
|
|
15399
|
+
...failClosed ? { [SEARCH_ABORT_FAIL_CLOSED]: true } : {},
|
|
14965
15400
|
getRecords: async (params) => {
|
|
14966
15401
|
const response = await client.getRecords(params);
|
|
14967
15402
|
if (response.searchAborted) {
|
|
@@ -15029,6 +15464,15 @@ async function resolveRelativeDateExecutionPlan(stmt, client, cacheContext) {
|
|
|
15029
15464
|
cacheContext
|
|
15030
15465
|
);
|
|
15031
15466
|
return decomposeRelativeDatePrefilter(select, resolver);
|
|
15467
|
+
},
|
|
15468
|
+
joinServerFunctionPlan: async (select) => {
|
|
15469
|
+
const metadata = await loadTypedPushdownMeta(select, client, cacheContext);
|
|
15470
|
+
const runtimePlan = buildRuntimeJoinPushdownPlan(select, metadata);
|
|
15471
|
+
if (runtimePlan === null) return null;
|
|
15472
|
+
if (isJoinServerFunctionFetchPlan(runtimePlan.joinPlan)) {
|
|
15473
|
+
boundJoinRuntimePlans.set(select, runtimePlan);
|
|
15474
|
+
}
|
|
15475
|
+
return runtimePlan.joinPlan;
|
|
15032
15476
|
}
|
|
15033
15477
|
});
|
|
15034
15478
|
}
|
|
@@ -15446,7 +15890,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
15446
15890
|
const statementClient = wrapClientWithSearchAbort(
|
|
15447
15891
|
countedClient,
|
|
15448
15892
|
searchAbortCollector,
|
|
15449
|
-
info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH"
|
|
15893
|
+
info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH" || statementContainsOuterJoin(statements[i])
|
|
15450
15894
|
);
|
|
15451
15895
|
const cursorScope = wrapClientWithCursorScope(statementClient);
|
|
15452
15896
|
const outcome = await runWithDeadline(
|
|
@@ -16653,6 +17097,7 @@ function extractMainTypedPushdownCandidate(stmt) {
|
|
|
16653
17097
|
if (!stmt.from.alias) return null;
|
|
16654
17098
|
return extractTypedPushdownCandidates(stmt.where, { tableAlias: stmt.from.alias });
|
|
16655
17099
|
}
|
|
17100
|
+
var boundJoinRuntimePlans = /* @__PURE__ */ new WeakMap();
|
|
16656
17101
|
function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
16657
17102
|
if (stmt.joins.length === 0 || stmt.where === null || stmt.joins.some((join2) => join2.type !== "INNER")) {
|
|
16658
17103
|
return null;
|
|
@@ -16673,11 +17118,17 @@ function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
|
16673
17118
|
]),
|
|
16674
17119
|
fieldOptions: metadata.fieldOptionsByApp.get(table.appId)
|
|
16675
17120
|
}));
|
|
16676
|
-
const
|
|
17121
|
+
const staticPlan = buildJoinPushdownPlan(stmt.where, sources);
|
|
17122
|
+
const plan = bindJoinServerFunctionFetches(staticPlan, sources);
|
|
16677
17123
|
const conditionsByAlias = /* @__PURE__ */ new Map();
|
|
16678
17124
|
const queriesByAlias = /* @__PURE__ */ new Map();
|
|
17125
|
+
for (const [alias, query] of plan.fetchQueriesByAlias) {
|
|
17126
|
+
queriesByAlias.set(alias, query);
|
|
17127
|
+
}
|
|
16679
17128
|
for (const item of plan.items) {
|
|
16680
|
-
queriesByAlias.
|
|
17129
|
+
if (!queriesByAlias.has(item.targetAlias)) {
|
|
17130
|
+
queriesByAlias.set(item.targetAlias, serializeJoinPushdownItem(item, sources));
|
|
17131
|
+
}
|
|
16681
17132
|
conditionsByAlias.set(item.targetAlias, item.predicate);
|
|
16682
17133
|
}
|
|
16683
17134
|
const mainAlias = stmt.from.alias;
|
|
@@ -17319,15 +17770,24 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17319
17770
|
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
17320
17771
|
);
|
|
17321
17772
|
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
17322
|
-
const
|
|
17773
|
+
const preboundJoinPlan = boundJoinRuntimePlans.get(stmt);
|
|
17774
|
+
if (preboundJoinPlan && !isJoinServerFunctionFetchPlan(preboundJoinPlan.joinPlan)) {
|
|
17775
|
+
throw new Error("InternalError: JOIN server-function fetch binding changed before records API.");
|
|
17776
|
+
}
|
|
17777
|
+
const pushdownPlan = preboundJoinPlan ?? buildRuntimeJoinPushdownPlan(stmt, pushdownMeta) ?? buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
17323
17778
|
validateKlikePushdownPlan(pushdownPlan);
|
|
17779
|
+
const runtimeJoinPlan = "joinPlan" in pushdownPlan ? pushdownPlan : null;
|
|
17780
|
+
const boundServerFunctionPlan = runtimeJoinPlan && isJoinServerFunctionFetchPlan(runtimeJoinPlan.joinPlan) ? runtimeJoinPlan : null;
|
|
17324
17781
|
const fetchClient = client;
|
|
17325
17782
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
17326
17783
|
const tableConditions = pushdownPlan.joinConditions;
|
|
17327
17784
|
if (prefilterPlan && allowOriginalWherePushdown) {
|
|
17328
17785
|
throw new Error("internal error: relative-date prefilter must disable original WHERE pushdown.");
|
|
17329
17786
|
}
|
|
17330
|
-
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : mainPushDown;
|
|
17787
|
+
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : boundServerFunctionPlan ? null : mainPushDown;
|
|
17788
|
+
const mainBoundQuery = boundServerFunctionPlan?.queriesByAlias.get(
|
|
17789
|
+
stmt.from.alias
|
|
17790
|
+
) ?? "";
|
|
17331
17791
|
const scalarCache = await resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
17332
17792
|
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
17333
17793
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
@@ -17340,8 +17800,9 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17340
17800
|
options.onLimitReached ?? "error",
|
|
17341
17801
|
warnings,
|
|
17342
17802
|
mainFetchCondition,
|
|
17343
|
-
allowOriginalWherePushdown,
|
|
17344
|
-
plainGroupByPlan
|
|
17803
|
+
boundServerFunctionPlan ? false : allowOriginalWherePushdown,
|
|
17804
|
+
plainGroupByPlan,
|
|
17805
|
+
mainBoundQuery
|
|
17345
17806
|
);
|
|
17346
17807
|
const parallelJoins = [];
|
|
17347
17808
|
const onOptJoins = [];
|
|
@@ -17350,8 +17811,9 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17350
17811
|
parallelJoins.push({ join: join2, promise: Promise.resolve([]) });
|
|
17351
17812
|
continue;
|
|
17352
17813
|
}
|
|
17353
|
-
const
|
|
17354
|
-
|
|
17814
|
+
const boundQuery = join2.table.alias ? boundServerFunctionPlan?.queriesByAlias.get(join2.table.alias) ?? "" : "";
|
|
17815
|
+
const jCond = boundServerFunctionPlan ? null : join2.table.alias ? tableConditions.get(join2.table.alias) ?? null : null;
|
|
17816
|
+
if (jCond !== null || boundQuery !== "") {
|
|
17355
17817
|
parallelJoins.push({
|
|
17356
17818
|
join: join2,
|
|
17357
17819
|
promise: fetchTableRecordsForFullScan(
|
|
@@ -17365,7 +17827,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17365
17827
|
warnings,
|
|
17366
17828
|
jCond,
|
|
17367
17829
|
true,
|
|
17368
|
-
plainGroupByPlan
|
|
17830
|
+
plainGroupByPlan,
|
|
17831
|
+
boundQuery
|
|
17369
17832
|
)
|
|
17370
17833
|
});
|
|
17371
17834
|
} else {
|
|
@@ -17425,7 +17888,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17425
17888
|
havingFieldSemanticsResolver,
|
|
17426
17889
|
aggregateSortKindResolver,
|
|
17427
17890
|
appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
|
|
17428
|
-
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : {},
|
|
17891
|
+
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : boundServerFunctionPlan ? { residualWhere: boundServerFunctionPlan.joinPlan.residualWhere } : {},
|
|
17429
17892
|
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt),
|
|
17430
17893
|
plainGroupByPlan
|
|
17431
17894
|
});
|
|
@@ -21133,8 +21596,11 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
21133
21596
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
21134
21597
|
}
|
|
21135
21598
|
capabilities.set(select, capability);
|
|
21136
|
-
const
|
|
21137
|
-
const joinPushdownPlan = buildRuntimeJoinPushdownPlan(
|
|
21599
|
+
const preboundJoinPushdownPlan = boundJoinRuntimePlans.get(select);
|
|
21600
|
+
const joinPushdownPlan = preboundJoinPushdownPlan ?? buildRuntimeJoinPushdownPlan(
|
|
21601
|
+
select,
|
|
21602
|
+
await loadTypedPushdownMeta(select, tracedClient, cacheContext)
|
|
21603
|
+
);
|
|
21138
21604
|
if (joinPushdownPlan) explainJoinPushdownPlans.set(select, joinPushdownPlan);
|
|
21139
21605
|
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
21140
21606
|
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
@@ -21372,9 +21838,15 @@ function relativeDateExplainLines(plan) {
|
|
|
21372
21838
|
if (!plan.hasServerOnlyWhereFunction) return [];
|
|
21373
21839
|
if (!plan.allowed && plan.rejection) {
|
|
21374
21840
|
const label = isRelativeDateFunctionName(plan.rejection.functionName) ? "relative date function" : "kintone function";
|
|
21841
|
+
const rejectedNode2 = plan.nodes[plan.nodes.length - 1];
|
|
21842
|
+
const detail = rejectedNode2?.capability?.reasons.find(
|
|
21843
|
+
(reason) => reason.functionName === plan.rejection.functionName
|
|
21844
|
+
);
|
|
21845
|
+
const target = rejectedNode2 ? findServerFunctionExplainTarget(rejectedNode2.source, plan.rejection.functionName) : null;
|
|
21375
21846
|
return [
|
|
21376
21847
|
` ${label}: ${plan.rejection.functionName}`,
|
|
21377
21848
|
" plan status: rejected",
|
|
21849
|
+
` target alias / field: ${target?.alias ?? "(unknown)"} / ${detail?.field ?? target?.field ?? "(unknown)"}`,
|
|
21378
21850
|
` reason: ${plan.rejection.reasonCodes.join(", ")}`,
|
|
21379
21851
|
" client evaluation: forbidden",
|
|
21380
21852
|
" records/cursor/mutation API during EXPLAIN: none"
|
|
@@ -21382,6 +21854,41 @@ function relativeDateExplainLines(plan) {
|
|
|
21382
21854
|
}
|
|
21383
21855
|
const lines = [];
|
|
21384
21856
|
for (const node of plan.nodes) {
|
|
21857
|
+
const joinServerFunctionPlan = node.joinServerFunctionPlan;
|
|
21858
|
+
if (node.allowed && node.allowForm === "JOIN_SERVER_FUNCTION_EXACT" && joinServerFunctionPlan) {
|
|
21859
|
+
const variant = node.joinServerFunctionVariant === "WHOLE_WHERE_EXACT" ? "whole-WHERE" : "leaf";
|
|
21860
|
+
lines.push(
|
|
21861
|
+
` allow form: JOIN_SERVER_FUNCTION_EXACT (${variant})`
|
|
21862
|
+
);
|
|
21863
|
+
for (const consumption of joinServerFunctionPlan.serverFunctionConsumptions) {
|
|
21864
|
+
for (const leaf of consumption.functionLeaves) {
|
|
21865
|
+
const functionName = serverFunctionNameOfExplainLeaf(leaf);
|
|
21866
|
+
const field = leaf.left.type === "FIELD" ? leaf.left.field : "(unknown)";
|
|
21867
|
+
lines.push(
|
|
21868
|
+
` ${serverFunctionLabel(functionName)}: ${functionName}`,
|
|
21869
|
+
` ${serverFunctionEvaluationLabel(functionName)}: kintone server exact JOIN prefilter`,
|
|
21870
|
+
` target alias / APP: ${consumption.targetAlias} / APP${consumption.appId}`,
|
|
21871
|
+
` field: ${field}`,
|
|
21872
|
+
` function leaf relation: ${consumption.relation}`,
|
|
21873
|
+
` consumption: ${consumption.consumption}`
|
|
21874
|
+
);
|
|
21875
|
+
}
|
|
21876
|
+
}
|
|
21877
|
+
lines.push(
|
|
21878
|
+
` client residual: ${joinServerFunctionPlan.residualWhere === null ? "(none)" : renderRelativeDateResidualWhere(joinServerFunctionPlan.residualWhere)}`
|
|
21879
|
+
);
|
|
21880
|
+
if (joinServerFunctionPlan.adoptedServerFunctionOccurrences.some(
|
|
21881
|
+
isRelativeDateFunctionName
|
|
21882
|
+
)) {
|
|
21883
|
+
lines.push(" relative date client evaluations: 0");
|
|
21884
|
+
}
|
|
21885
|
+
if (joinServerFunctionPlan.adoptedServerFunctionOccurrences.some(
|
|
21886
|
+
(name) => !isRelativeDateFunctionName(name)
|
|
21887
|
+
)) {
|
|
21888
|
+
lines.push(" kintone function client evaluations: 0");
|
|
21889
|
+
}
|
|
21890
|
+
continue;
|
|
21891
|
+
}
|
|
21385
21892
|
const fullScanExactPlan = node.fullScanExactPlan;
|
|
21386
21893
|
if (node.allowed && node.allowForm === "FULL_SCAN_EXACT" && fullScanExactPlan) {
|
|
21387
21894
|
for (const leaf of fullScanExactPlan.prefilterPlan.exactRelativeLeaves) {
|
|
@@ -21466,6 +21973,36 @@ function relativeDateExplainLines(plan) {
|
|
|
21466
21973
|
}
|
|
21467
21974
|
return lines;
|
|
21468
21975
|
}
|
|
21976
|
+
function findServerFunctionExplainTarget(source, functionName) {
|
|
21977
|
+
const where = "where" in source ? source.where : null;
|
|
21978
|
+
let found = null;
|
|
21979
|
+
const visit = (value) => {
|
|
21980
|
+
if (found || value === null || typeof value !== "object") return;
|
|
21981
|
+
if (Array.isArray(value)) {
|
|
21982
|
+
value.forEach(visit);
|
|
21983
|
+
return;
|
|
21984
|
+
}
|
|
21985
|
+
const node = value;
|
|
21986
|
+
if (node["type"] === "BINARY") {
|
|
21987
|
+
const left = node["left"];
|
|
21988
|
+
const right = node["right"];
|
|
21989
|
+
const directlyMatches = right?.["type"] === "KINTONE_FUNC" && right["name"] === functionName;
|
|
21990
|
+
const listMatches = right?.["type"] === "IN_LIST" && Array.isArray(right["values"]) && right["values"].some(
|
|
21991
|
+
(entry) => entry !== null && typeof entry === "object" && entry["type"] === "KINTONE_FUNC" && entry["name"] === functionName
|
|
21992
|
+
);
|
|
21993
|
+
if ((directlyMatches || listMatches) && left?.["type"] === "FIELD" && typeof left["field"] === "string") {
|
|
21994
|
+
found = {
|
|
21995
|
+
alias: typeof left["tableAlias"] === "string" ? left["tableAlias"] : null,
|
|
21996
|
+
field: left["field"]
|
|
21997
|
+
};
|
|
21998
|
+
return;
|
|
21999
|
+
}
|
|
22000
|
+
}
|
|
22001
|
+
Object.values(node).forEach(visit);
|
|
22002
|
+
};
|
|
22003
|
+
visit(where);
|
|
22004
|
+
return found;
|
|
22005
|
+
}
|
|
21469
22006
|
function serverFunctionLabel(functionName) {
|
|
21470
22007
|
return isRelativeDateFunctionName(functionName) ? "relative date function" : "kintone function";
|
|
21471
22008
|
}
|
|
@@ -21883,10 +22420,22 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21883
22420
|
lines.push(" join pushdown plan: applied (runtime metadata resolved)");
|
|
21884
22421
|
lines.push(" runtime plan timing: variables/subqueries resolved -> metadata resolved -> immutable plan");
|
|
21885
22422
|
lines.push(" EXPLAIN unresolved subqueries: not applied (records API is not called)");
|
|
21886
|
-
|
|
22423
|
+
if (runtimeJoinPlan.joinPlan.serverFunctionCandidate) {
|
|
22424
|
+
lines.push(
|
|
22425
|
+
` allow form: JOIN_SERVER_FUNCTION_EXACT (${runtimeJoinPlan.joinPlan.serverFunctionCandidate.variant === "WHOLE_WHERE_EXACT" ? "whole-WHERE" : "leaf"})`
|
|
22426
|
+
);
|
|
22427
|
+
}
|
|
22428
|
+
lines.push(
|
|
22429
|
+
` client residual: ${runtimeJoinPlan.joinPlan.residualWhere === null ? "(none)" : renderRelativeDateResidualWhere(runtimeJoinPlan.joinPlan.residualWhere)}`
|
|
22430
|
+
);
|
|
21887
22431
|
lines.push(` KLIKE applied nodes: ${runtimeJoinPlan.joinPlan.appliedKlikes.size}`);
|
|
21888
|
-
|
|
21889
|
-
|
|
22432
|
+
lines.push(
|
|
22433
|
+
` KLIKE unapplied nodes: ${runtimeJoinPlan.joinPlan.allKlikes.length - runtimeJoinPlan.joinPlan.appliedKlikes.size}`
|
|
22434
|
+
);
|
|
22435
|
+
if (runtimeJoinPlan.joinPlan.residualWhere !== null) {
|
|
22436
|
+
for (const rejection of runtimeJoinPlan.joinPlan.rejections) {
|
|
22437
|
+
lines.push(` join pushdown not applied: ${rejection.reason}`);
|
|
22438
|
+
}
|
|
21890
22439
|
}
|
|
21891
22440
|
} else {
|
|
21892
22441
|
const reason = stmt.joins.some((join2) => join2.type !== "INNER") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
|
|
@@ -21901,15 +22450,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21901
22450
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
21902
22451
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
21903
22452
|
const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
|
|
21904
|
-
const
|
|
22453
|
+
const mainBoundQuery = stmt.from.alias ? runtimeJoinPlan?.queriesByAlias.get(stmt.from.alias) : void 0;
|
|
22454
|
+
const mainQ = mainBoundQuery || (mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)");
|
|
21905
22455
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
21906
22456
|
lines.push(` kintone query: ${mainQ}`);
|
|
21907
22457
|
const mainJoinItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21908
22458
|
(item) => item.targetAlias === stmt.from.alias
|
|
21909
22459
|
);
|
|
21910
|
-
|
|
21911
|
-
|
|
21912
|
-
|
|
22460
|
+
const mainFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
|
|
22461
|
+
(consumption) => consumption.targetAlias === stmt.from.alias
|
|
22462
|
+
);
|
|
22463
|
+
if (mainJoinItem || mainFunctionConsumption) {
|
|
22464
|
+
lines.push(` pushdown applied: ${mainBoundQuery}`);
|
|
22465
|
+
lines.push(` relation: ${mainJoinItem?.relation ?? "exact"}`);
|
|
21913
22466
|
} else if (!runtimeJoinPlan && mainCandidate !== null) {
|
|
21914
22467
|
lines.push(` pushdown candidate: ${whereToKintone(mainCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21915
22468
|
}
|
|
@@ -21920,15 +22473,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21920
22473
|
const joinType = join2.type === "INNER" ? "JOIN" : `${join2.type} JOIN`;
|
|
21921
22474
|
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
21922
22475
|
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
21923
|
-
const
|
|
22476
|
+
const joinBoundQuery = join2.table.alias ? runtimeJoinPlan?.queriesByAlias.get(join2.table.alias) : void 0;
|
|
22477
|
+
const joinQ = joinBoundQuery || (joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)");
|
|
21924
22478
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
21925
22479
|
lines.push(` kintone query: ${joinQ}`);
|
|
21926
22480
|
const joinPlanItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21927
22481
|
(item) => item.targetAlias === join2.table.alias
|
|
21928
22482
|
);
|
|
21929
|
-
|
|
21930
|
-
|
|
21931
|
-
|
|
22483
|
+
const joinFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
|
|
22484
|
+
(consumption) => consumption.targetAlias === join2.table.alias
|
|
22485
|
+
);
|
|
22486
|
+
if (joinPlanItem || joinFunctionConsumption) {
|
|
22487
|
+
lines.push(` pushdown applied: ${joinBoundQuery}`);
|
|
22488
|
+
lines.push(` relation: ${joinPlanItem?.relation ?? "exact"}`);
|
|
21932
22489
|
} else if (!runtimeJoinPlan && joinCandidate !== null) {
|
|
21933
22490
|
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21934
22491
|
}
|