@rex0220/kintone-sql-tools 3.27.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 +685 -155
- 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 +7 -7
- package/dist-engine/meta/cjs.json +43 -23
- package/dist-engine/meta/esm.json +43 -23
- package/dist-engine/meta/umd.json +43 -23
- package/dist-mcp/ksql-mcp.js +689 -161
- 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;
|
|
@@ -11233,6 +11369,30 @@ function classifyJoinPushdownLeaf(predicate, sources) {
|
|
|
11233
11369
|
const relation = classifySupportedLeaf(predicate, owner, fieldType);
|
|
11234
11370
|
return relation === "unsafe" ? unsafe() : Object.freeze({ relation, owner });
|
|
11235
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
|
+
}
|
|
11236
11396
|
function buildJoinPushdownPlan(where, sources) {
|
|
11237
11397
|
const allKlikes = /* @__PURE__ */ new Set();
|
|
11238
11398
|
collectKlikes2(where, allKlikes);
|
|
@@ -11243,14 +11403,369 @@ function buildJoinPushdownPlan(where, sources) {
|
|
|
11243
11403
|
predicate: fragment.predicate,
|
|
11244
11404
|
relation: fragment.relation
|
|
11245
11405
|
}));
|
|
11406
|
+
const serverFunctionFoundation = buildServerFunctionFoundation(where, sources);
|
|
11246
11407
|
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
11247
|
-
|
|
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
|
+
}
|
|
11248
11413
|
return Object.freeze({
|
|
11249
11414
|
items: Object.freeze(items),
|
|
11250
11415
|
appliedKlikes,
|
|
11251
11416
|
allKlikes: Object.freeze([...allKlikes]),
|
|
11252
|
-
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
|
+
});
|
|
11253
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
|
|
11494
|
+
});
|
|
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) ?? "";
|
|
11254
11769
|
}
|
|
11255
11770
|
function collectRejections(where, sources) {
|
|
11256
11771
|
if (where === null) return [];
|
|
@@ -11322,6 +11837,9 @@ function assertPredicateOwnership(predicate, item, sources) {
|
|
|
11322
11837
|
case "GROUP":
|
|
11323
11838
|
assertPredicateOwnership(predicate.expr, item, sources);
|
|
11324
11839
|
return;
|
|
11840
|
+
case "NOT":
|
|
11841
|
+
assertPredicateOwnership(predicate.expr, item, sources);
|
|
11842
|
+
return;
|
|
11325
11843
|
case "BINARY": {
|
|
11326
11844
|
if (predicate.left.type !== "FIELD" || containsFieldReference(predicate.right)) {
|
|
11327
11845
|
throw joinOwnershipError(item, "predicate contains a non-target or RHS field reference");
|
|
@@ -11332,7 +11850,6 @@ function assertPredicateOwnership(predicate, item, sources) {
|
|
|
11332
11850
|
}
|
|
11333
11851
|
return;
|
|
11334
11852
|
}
|
|
11335
|
-
case "NOT":
|
|
11336
11853
|
case "NULL_CHECK":
|
|
11337
11854
|
case "EXISTS":
|
|
11338
11855
|
case "BOOLEAN":
|
|
@@ -13138,133 +13655,6 @@ function deepClone(value, seen = /* @__PURE__ */ new Map()) {
|
|
|
13138
13655
|
return clone;
|
|
13139
13656
|
}
|
|
13140
13657
|
|
|
13141
|
-
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
13142
|
-
function buildRelativeDateFullScanExactPlan(input) {
|
|
13143
|
-
const {
|
|
13144
|
-
select,
|
|
13145
|
-
selectMode,
|
|
13146
|
-
capability,
|
|
13147
|
-
context,
|
|
13148
|
-
serializedWholeWhere,
|
|
13149
|
-
relativeFunctionNames
|
|
13150
|
-
} = input;
|
|
13151
|
-
if (select.where === null) return null;
|
|
13152
|
-
if (select.from.appId <= 0 || select.from.cteName !== null) return null;
|
|
13153
|
-
if (select.from.subtableCode) return null;
|
|
13154
|
-
if (select.joins.length > 0) return null;
|
|
13155
|
-
if (!context.allowFullScanExact) return null;
|
|
13156
|
-
if (select.orderMode === "KINTONE_NATIVE") return null;
|
|
13157
|
-
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
13158
|
-
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
13159
|
-
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
13160
|
-
const occurrences = serverOnlyFunctionOccurrencesInWhere(select.where);
|
|
13161
|
-
if (occurrences.length === 0) return null;
|
|
13162
|
-
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
13163
|
-
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
13164
|
-
return null;
|
|
13165
|
-
}
|
|
13166
|
-
const prefilterPlan = {
|
|
13167
|
-
prefilterWhere: select.where,
|
|
13168
|
-
residualWhere: null,
|
|
13169
|
-
exactRelativeLeaves: collectExactServerFunctionLeaves(select.where),
|
|
13170
|
-
relativeFunctionNames: new Set(occurrences),
|
|
13171
|
-
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
13172
|
-
capability: capability.capability,
|
|
13173
|
-
reasons: capability.reasons
|
|
13174
|
-
};
|
|
13175
|
-
const plan = {
|
|
13176
|
-
allowForm: "FULL_SCAN_EXACT",
|
|
13177
|
-
clientWhereEvaluation: false,
|
|
13178
|
-
serializedWholeWhere,
|
|
13179
|
-
prefilterPlan
|
|
13180
|
-
};
|
|
13181
|
-
assertRelativeDateFullScanExactPlan(plan, input, occurrences);
|
|
13182
|
-
return plan;
|
|
13183
|
-
}
|
|
13184
|
-
function assertRelativeDateFullScanExactPlan(plan, input, occurrences) {
|
|
13185
|
-
if (plan.allowForm !== "FULL_SCAN_EXACT") {
|
|
13186
|
-
throw new Error("FULL_SCAN_EXACT invariant: allowForm");
|
|
13187
|
-
}
|
|
13188
|
-
if (plan.clientWhereEvaluation !== false) {
|
|
13189
|
-
throw new Error("FULL_SCAN_EXACT invariant: clientWhereEvaluation");
|
|
13190
|
-
}
|
|
13191
|
-
if (input.capability.capability !== "EXACT_PUSHDOWN") {
|
|
13192
|
-
throw new Error("FULL_SCAN_EXACT invariant: capability");
|
|
13193
|
-
}
|
|
13194
|
-
if (input.select.where === null || plan.prefilterPlan.prefilterWhere !== input.select.where) {
|
|
13195
|
-
throw new Error("FULL_SCAN_EXACT invariant: whole WHERE identity");
|
|
13196
|
-
}
|
|
13197
|
-
if (plan.prefilterPlan.residualWhere !== null) {
|
|
13198
|
-
throw new Error("FULL_SCAN_EXACT invariant: residualWhere");
|
|
13199
|
-
}
|
|
13200
|
-
if (plan.prefilterPlan.capability !== "EXACT_PUSHDOWN") {
|
|
13201
|
-
throw new Error("FULL_SCAN_EXACT invariant: transport capability");
|
|
13202
|
-
}
|
|
13203
|
-
if (!serializedMultisetContains(plan.serializedWholeWhere, occurrences)) {
|
|
13204
|
-
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
13205
|
-
}
|
|
13206
|
-
}
|
|
13207
|
-
function serverOnlyFunctionOccurrencesInWhere(where) {
|
|
13208
|
-
const names = [];
|
|
13209
|
-
const visit = (node) => {
|
|
13210
|
-
if (Array.isArray(node)) {
|
|
13211
|
-
node.forEach(visit);
|
|
13212
|
-
return;
|
|
13213
|
-
}
|
|
13214
|
-
if (node === null || typeof node !== "object") return;
|
|
13215
|
-
const value = node;
|
|
13216
|
-
if (value["type"] === "SELECT") return;
|
|
13217
|
-
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isServerOnlyWhereFunctionName(value["name"])) {
|
|
13218
|
-
names.push(value["name"]);
|
|
13219
|
-
return;
|
|
13220
|
-
}
|
|
13221
|
-
Object.values(value).forEach(visit);
|
|
13222
|
-
};
|
|
13223
|
-
visit(where);
|
|
13224
|
-
return names;
|
|
13225
|
-
}
|
|
13226
|
-
function collectExactServerFunctionLeaves(where) {
|
|
13227
|
-
const leaves = [];
|
|
13228
|
-
const visit = (node) => {
|
|
13229
|
-
switch (node.type) {
|
|
13230
|
-
case "BINARY":
|
|
13231
|
-
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)) {
|
|
13232
|
-
leaves.push(node);
|
|
13233
|
-
}
|
|
13234
|
-
return;
|
|
13235
|
-
case "LOGICAL":
|
|
13236
|
-
visit(node.left);
|
|
13237
|
-
visit(node.right);
|
|
13238
|
-
return;
|
|
13239
|
-
case "NOT":
|
|
13240
|
-
case "GROUP":
|
|
13241
|
-
visit(node.expr);
|
|
13242
|
-
return;
|
|
13243
|
-
case "EXISTS":
|
|
13244
|
-
case "NULL_CHECK":
|
|
13245
|
-
case "BOOLEAN":
|
|
13246
|
-
return;
|
|
13247
|
-
}
|
|
13248
|
-
};
|
|
13249
|
-
visit(where);
|
|
13250
|
-
return leaves;
|
|
13251
|
-
}
|
|
13252
|
-
function sameOccurrenceList(actual, expected) {
|
|
13253
|
-
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
13254
|
-
}
|
|
13255
|
-
function serializedMultisetContains(query, expectedNames) {
|
|
13256
|
-
const expected = /* @__PURE__ */ new Map();
|
|
13257
|
-
for (const name of expectedNames) {
|
|
13258
|
-
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
13259
|
-
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
13260
|
-
}
|
|
13261
|
-
for (const [name, count] of expected) {
|
|
13262
|
-
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
13263
|
-
if ((matches?.length ?? 0) < count) return false;
|
|
13264
|
-
}
|
|
13265
|
-
return true;
|
|
13266
|
-
}
|
|
13267
|
-
|
|
13268
13658
|
// src/core/optimization/relativeDatePushdownGuard.ts
|
|
13269
13659
|
function relativeDateFunctionNamesInNode(node, stopAtNestedSelect) {
|
|
13270
13660
|
const names = [];
|
|
@@ -13485,6 +13875,11 @@ function serializationContainsFunctions(query, names) {
|
|
|
13485
13875
|
function allowRelativeDatePrefilterPlan(select, decomposition) {
|
|
13486
13876
|
return decomposition.eligible === true && resolveSelectMode(select) === "FULL_SCAN" && select.orderMode !== "KINTONE_NATIVE" && select.from.cteName === null && !select.from.subtableCode && select.joins.length === 0;
|
|
13487
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
|
+
}
|
|
13488
13883
|
function rejectedNode(candidate) {
|
|
13489
13884
|
return {
|
|
13490
13885
|
kind: candidate.kind,
|
|
@@ -13584,6 +13979,14 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13584
13979
|
allowed2 = true;
|
|
13585
13980
|
}
|
|
13586
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
|
+
}
|
|
13587
13990
|
const node2 = {
|
|
13588
13991
|
kind: candidate.kind,
|
|
13589
13992
|
source: candidate.source,
|
|
@@ -13594,6 +13997,11 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13594
13997
|
restQuery: restQuery2,
|
|
13595
13998
|
...prefilterPlan ? { prefilterPlan, phase2PrefilterEligible } : {},
|
|
13596
13999
|
...fullScanExactPlan ? { fullScanExactPlan, allowForm: fullScanExactPlan.allowForm } : {},
|
|
14000
|
+
...joinServerFunctionPlan ? {
|
|
14001
|
+
joinServerFunctionPlan,
|
|
14002
|
+
allowForm: "JOIN_SERVER_FUNCTION_EXACT",
|
|
14003
|
+
joinServerFunctionVariant: joinServerFunctionPlan.serverFunctionCandidate.variant
|
|
14004
|
+
} : {},
|
|
13597
14005
|
clientWhereEvaluation: !allowed2,
|
|
13598
14006
|
allowed: allowed2
|
|
13599
14007
|
};
|
|
@@ -13685,7 +14093,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13685
14093
|
}
|
|
13686
14094
|
const occurrences = collectServerFunctionOccurrences(stmt.where);
|
|
13687
14095
|
if (occurrences.length === 0) return reject(["NO_RELATIVE_DATE"]);
|
|
13688
|
-
const spine =
|
|
14096
|
+
const spine = collectServerFunctionLeavesOnAndSpine2(stmt.where, resolveField2);
|
|
13689
14097
|
if (!spine.ok) return reject(spine.reasonCodes);
|
|
13690
14098
|
if (!sameRelativeMultiset(occurrences, spine.leaves)) {
|
|
13691
14099
|
return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
@@ -13763,7 +14171,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13763
14171
|
}
|
|
13764
14172
|
};
|
|
13765
14173
|
}
|
|
13766
|
-
function
|
|
14174
|
+
function collectServerFunctionLeavesOnAndSpine2(where, resolveField2) {
|
|
13767
14175
|
const leaves = [];
|
|
13768
14176
|
let failure = null;
|
|
13769
14177
|
const visit = (node) => {
|
|
@@ -15056,6 +15464,15 @@ async function resolveRelativeDateExecutionPlan(stmt, client, cacheContext) {
|
|
|
15056
15464
|
cacheContext
|
|
15057
15465
|
);
|
|
15058
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;
|
|
15059
15476
|
}
|
|
15060
15477
|
});
|
|
15061
15478
|
}
|
|
@@ -16680,6 +17097,7 @@ function extractMainTypedPushdownCandidate(stmt) {
|
|
|
16680
17097
|
if (!stmt.from.alias) return null;
|
|
16681
17098
|
return extractTypedPushdownCandidates(stmt.where, { tableAlias: stmt.from.alias });
|
|
16682
17099
|
}
|
|
17100
|
+
var boundJoinRuntimePlans = /* @__PURE__ */ new WeakMap();
|
|
16683
17101
|
function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
16684
17102
|
if (stmt.joins.length === 0 || stmt.where === null || stmt.joins.some((join2) => join2.type !== "INNER")) {
|
|
16685
17103
|
return null;
|
|
@@ -16700,11 +17118,17 @@ function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
|
16700
17118
|
]),
|
|
16701
17119
|
fieldOptions: metadata.fieldOptionsByApp.get(table.appId)
|
|
16702
17120
|
}));
|
|
16703
|
-
const
|
|
17121
|
+
const staticPlan = buildJoinPushdownPlan(stmt.where, sources);
|
|
17122
|
+
const plan = bindJoinServerFunctionFetches(staticPlan, sources);
|
|
16704
17123
|
const conditionsByAlias = /* @__PURE__ */ new Map();
|
|
16705
17124
|
const queriesByAlias = /* @__PURE__ */ new Map();
|
|
17125
|
+
for (const [alias, query] of plan.fetchQueriesByAlias) {
|
|
17126
|
+
queriesByAlias.set(alias, query);
|
|
17127
|
+
}
|
|
16706
17128
|
for (const item of plan.items) {
|
|
16707
|
-
queriesByAlias.
|
|
17129
|
+
if (!queriesByAlias.has(item.targetAlias)) {
|
|
17130
|
+
queriesByAlias.set(item.targetAlias, serializeJoinPushdownItem(item, sources));
|
|
17131
|
+
}
|
|
16708
17132
|
conditionsByAlias.set(item.targetAlias, item.predicate);
|
|
16709
17133
|
}
|
|
16710
17134
|
const mainAlias = stmt.from.alias;
|
|
@@ -17346,15 +17770,24 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17346
17770
|
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
17347
17771
|
);
|
|
17348
17772
|
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
17349
|
-
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);
|
|
17350
17778
|
validateKlikePushdownPlan(pushdownPlan);
|
|
17779
|
+
const runtimeJoinPlan = "joinPlan" in pushdownPlan ? pushdownPlan : null;
|
|
17780
|
+
const boundServerFunctionPlan = runtimeJoinPlan && isJoinServerFunctionFetchPlan(runtimeJoinPlan.joinPlan) ? runtimeJoinPlan : null;
|
|
17351
17781
|
const fetchClient = client;
|
|
17352
17782
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
17353
17783
|
const tableConditions = pushdownPlan.joinConditions;
|
|
17354
17784
|
if (prefilterPlan && allowOriginalWherePushdown) {
|
|
17355
17785
|
throw new Error("internal error: relative-date prefilter must disable original WHERE pushdown.");
|
|
17356
17786
|
}
|
|
17357
|
-
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
|
+
) ?? "";
|
|
17358
17791
|
const scalarCache = await resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
17359
17792
|
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
17360
17793
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
@@ -17367,8 +17800,9 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17367
17800
|
options.onLimitReached ?? "error",
|
|
17368
17801
|
warnings,
|
|
17369
17802
|
mainFetchCondition,
|
|
17370
|
-
allowOriginalWherePushdown,
|
|
17371
|
-
plainGroupByPlan
|
|
17803
|
+
boundServerFunctionPlan ? false : allowOriginalWherePushdown,
|
|
17804
|
+
plainGroupByPlan,
|
|
17805
|
+
mainBoundQuery
|
|
17372
17806
|
);
|
|
17373
17807
|
const parallelJoins = [];
|
|
17374
17808
|
const onOptJoins = [];
|
|
@@ -17377,8 +17811,9 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17377
17811
|
parallelJoins.push({ join: join2, promise: Promise.resolve([]) });
|
|
17378
17812
|
continue;
|
|
17379
17813
|
}
|
|
17380
|
-
const
|
|
17381
|
-
|
|
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 !== "") {
|
|
17382
17817
|
parallelJoins.push({
|
|
17383
17818
|
join: join2,
|
|
17384
17819
|
promise: fetchTableRecordsForFullScan(
|
|
@@ -17392,7 +17827,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17392
17827
|
warnings,
|
|
17393
17828
|
jCond,
|
|
17394
17829
|
true,
|
|
17395
|
-
plainGroupByPlan
|
|
17830
|
+
plainGroupByPlan,
|
|
17831
|
+
boundQuery
|
|
17396
17832
|
)
|
|
17397
17833
|
});
|
|
17398
17834
|
} else {
|
|
@@ -17452,7 +17888,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17452
17888
|
havingFieldSemanticsResolver,
|
|
17453
17889
|
aggregateSortKindResolver,
|
|
17454
17890
|
appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
|
|
17455
|
-
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : {},
|
|
17891
|
+
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : boundServerFunctionPlan ? { residualWhere: boundServerFunctionPlan.joinPlan.residualWhere } : {},
|
|
17456
17892
|
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt),
|
|
17457
17893
|
plainGroupByPlan
|
|
17458
17894
|
});
|
|
@@ -21160,8 +21596,11 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
21160
21596
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
21161
21597
|
}
|
|
21162
21598
|
capabilities.set(select, capability);
|
|
21163
|
-
const
|
|
21164
|
-
const joinPushdownPlan = buildRuntimeJoinPushdownPlan(
|
|
21599
|
+
const preboundJoinPushdownPlan = boundJoinRuntimePlans.get(select);
|
|
21600
|
+
const joinPushdownPlan = preboundJoinPushdownPlan ?? buildRuntimeJoinPushdownPlan(
|
|
21601
|
+
select,
|
|
21602
|
+
await loadTypedPushdownMeta(select, tracedClient, cacheContext)
|
|
21603
|
+
);
|
|
21165
21604
|
if (joinPushdownPlan) explainJoinPushdownPlans.set(select, joinPushdownPlan);
|
|
21166
21605
|
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
21167
21606
|
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
@@ -21399,9 +21838,15 @@ function relativeDateExplainLines(plan) {
|
|
|
21399
21838
|
if (!plan.hasServerOnlyWhereFunction) return [];
|
|
21400
21839
|
if (!plan.allowed && plan.rejection) {
|
|
21401
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;
|
|
21402
21846
|
return [
|
|
21403
21847
|
` ${label}: ${plan.rejection.functionName}`,
|
|
21404
21848
|
" plan status: rejected",
|
|
21849
|
+
` target alias / field: ${target?.alias ?? "(unknown)"} / ${detail?.field ?? target?.field ?? "(unknown)"}`,
|
|
21405
21850
|
` reason: ${plan.rejection.reasonCodes.join(", ")}`,
|
|
21406
21851
|
" client evaluation: forbidden",
|
|
21407
21852
|
" records/cursor/mutation API during EXPLAIN: none"
|
|
@@ -21409,6 +21854,41 @@ function relativeDateExplainLines(plan) {
|
|
|
21409
21854
|
}
|
|
21410
21855
|
const lines = [];
|
|
21411
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
|
+
}
|
|
21412
21892
|
const fullScanExactPlan = node.fullScanExactPlan;
|
|
21413
21893
|
if (node.allowed && node.allowForm === "FULL_SCAN_EXACT" && fullScanExactPlan) {
|
|
21414
21894
|
for (const leaf of fullScanExactPlan.prefilterPlan.exactRelativeLeaves) {
|
|
@@ -21493,6 +21973,36 @@ function relativeDateExplainLines(plan) {
|
|
|
21493
21973
|
}
|
|
21494
21974
|
return lines;
|
|
21495
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
|
+
}
|
|
21496
22006
|
function serverFunctionLabel(functionName) {
|
|
21497
22007
|
return isRelativeDateFunctionName(functionName) ? "relative date function" : "kintone function";
|
|
21498
22008
|
}
|
|
@@ -21910,10 +22420,22 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21910
22420
|
lines.push(" join pushdown plan: applied (runtime metadata resolved)");
|
|
21911
22421
|
lines.push(" runtime plan timing: variables/subqueries resolved -> metadata resolved -> immutable plan");
|
|
21912
22422
|
lines.push(" EXPLAIN unresolved subqueries: not applied (records API is not called)");
|
|
21913
|
-
|
|
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
|
+
);
|
|
21914
22431
|
lines.push(` KLIKE applied nodes: ${runtimeJoinPlan.joinPlan.appliedKlikes.size}`);
|
|
21915
|
-
|
|
21916
|
-
|
|
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
|
+
}
|
|
21917
22439
|
}
|
|
21918
22440
|
} else {
|
|
21919
22441
|
const reason = stmt.joins.some((join2) => join2.type !== "INNER") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
|
|
@@ -21928,15 +22450,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21928
22450
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
21929
22451
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
21930
22452
|
const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
|
|
21931
|
-
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)");
|
|
21932
22455
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
21933
22456
|
lines.push(` kintone query: ${mainQ}`);
|
|
21934
22457
|
const mainJoinItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21935
22458
|
(item) => item.targetAlias === stmt.from.alias
|
|
21936
22459
|
);
|
|
21937
|
-
|
|
21938
|
-
|
|
21939
|
-
|
|
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"}`);
|
|
21940
22466
|
} else if (!runtimeJoinPlan && mainCandidate !== null) {
|
|
21941
22467
|
lines.push(` pushdown candidate: ${whereToKintone(mainCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21942
22468
|
}
|
|
@@ -21947,15 +22473,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21947
22473
|
const joinType = join2.type === "INNER" ? "JOIN" : `${join2.type} JOIN`;
|
|
21948
22474
|
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
21949
22475
|
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
21950
|
-
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)");
|
|
21951
22478
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
21952
22479
|
lines.push(` kintone query: ${joinQ}`);
|
|
21953
22480
|
const joinPlanItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21954
22481
|
(item) => item.targetAlias === join2.table.alias
|
|
21955
22482
|
);
|
|
21956
|
-
|
|
21957
|
-
|
|
21958
|
-
|
|
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"}`);
|
|
21959
22489
|
} else if (!runtimeJoinPlan && joinCandidate !== null) {
|
|
21960
22490
|
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21961
22491
|
}
|