@rex0220/kintone-sql-tools 3.27.0 → 3.29.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/README.md +8 -2
- package/dist-cli/ksql.js +703 -166
- package/dist-engine/batch.d.ts +13 -0
- package/dist-engine/errors.d.ts +5 -0
- package/dist-engine/index.cjs +11 -11
- package/dist-engine/index.d.ts +3 -2
- package/dist-engine/index.mjs +11 -11
- package/dist-engine/ksql-engine.umd.js +11 -11
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +154 -42
- package/dist-engine/meta/esm.json +154 -41
- package/dist-engine/meta/umd.json +156 -44
- package/dist-engine/publicTypes.d.ts +73 -0
- package/dist-engine/query.d.ts +8 -0
- package/dist-mcp/ksql-mcp.js +716 -178
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +4 -3
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,15 +11403,370 @@ 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
|
+
});
|
|
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
|
|
11253
11494
|
});
|
|
11254
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
|
+
}
|
|
11255
11770
|
function collectRejections(where, sources) {
|
|
11256
11771
|
if (where === null) return [];
|
|
11257
11772
|
const reasons = /* @__PURE__ */ new Set();
|
|
@@ -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
|
}
|
|
@@ -15741,21 +16158,28 @@ async function runWithDeadline(work, remainingMs, onTimeout) {
|
|
|
15741
16158
|
}
|
|
15742
16159
|
}
|
|
15743
16160
|
function toBatchStatementError(e) {
|
|
16161
|
+
let error;
|
|
15744
16162
|
if (e instanceof ApplyWritePartialFailureError) {
|
|
15745
|
-
|
|
15746
|
-
}
|
|
15747
|
-
if (e instanceof Error) {
|
|
16163
|
+
error = { code: e.name, message: e.message, partialSuccess: e.partialSuccess };
|
|
16164
|
+
} else if (e instanceof Error) {
|
|
15748
16165
|
const name = e.name !== "Error" ? e.name : null;
|
|
15749
|
-
|
|
15750
|
-
}
|
|
15751
|
-
if (e !== null && typeof e === "object") {
|
|
16166
|
+
error = { code: name ?? codeFromMessagePrefix(e.message), message: e.message };
|
|
16167
|
+
} else if (e !== null && typeof e === "object") {
|
|
15752
16168
|
const obj = e;
|
|
15753
|
-
const
|
|
15754
|
-
const code = typeof obj.code === "string" && obj.code.length > 0 ? obj.code : codeFromMessagePrefix(
|
|
15755
|
-
|
|
16169
|
+
const message = typeof obj.message === "string" && obj.message.length > 0 ? obj.message : safeJsonStringify(e);
|
|
16170
|
+
const code = typeof obj.code === "string" && obj.code.length > 0 ? obj.code : codeFromMessagePrefix(message);
|
|
16171
|
+
error = { code, message };
|
|
16172
|
+
} else {
|
|
16173
|
+
const message = String(e);
|
|
16174
|
+
error = { code: codeFromMessagePrefix(message), message };
|
|
15756
16175
|
}
|
|
15757
|
-
|
|
15758
|
-
|
|
16176
|
+
Object.defineProperty(error, "cause", {
|
|
16177
|
+
value: e,
|
|
16178
|
+
enumerable: false,
|
|
16179
|
+
configurable: false,
|
|
16180
|
+
writable: false
|
|
16181
|
+
});
|
|
16182
|
+
return error;
|
|
15759
16183
|
}
|
|
15760
16184
|
function codeFromMessagePrefix(message) {
|
|
15761
16185
|
return message.match(/^([A-Za-z]+Error):/)?.[1] ?? "Error";
|
|
@@ -16680,6 +17104,7 @@ function extractMainTypedPushdownCandidate(stmt) {
|
|
|
16680
17104
|
if (!stmt.from.alias) return null;
|
|
16681
17105
|
return extractTypedPushdownCandidates(stmt.where, { tableAlias: stmt.from.alias });
|
|
16682
17106
|
}
|
|
17107
|
+
var boundJoinRuntimePlans = /* @__PURE__ */ new WeakMap();
|
|
16683
17108
|
function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
16684
17109
|
if (stmt.joins.length === 0 || stmt.where === null || stmt.joins.some((join2) => join2.type !== "INNER")) {
|
|
16685
17110
|
return null;
|
|
@@ -16700,11 +17125,17 @@ function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
|
16700
17125
|
]),
|
|
16701
17126
|
fieldOptions: metadata.fieldOptionsByApp.get(table.appId)
|
|
16702
17127
|
}));
|
|
16703
|
-
const
|
|
17128
|
+
const staticPlan = buildJoinPushdownPlan(stmt.where, sources);
|
|
17129
|
+
const plan = bindJoinServerFunctionFetches(staticPlan, sources);
|
|
16704
17130
|
const conditionsByAlias = /* @__PURE__ */ new Map();
|
|
16705
17131
|
const queriesByAlias = /* @__PURE__ */ new Map();
|
|
17132
|
+
for (const [alias, query] of plan.fetchQueriesByAlias) {
|
|
17133
|
+
queriesByAlias.set(alias, query);
|
|
17134
|
+
}
|
|
16706
17135
|
for (const item of plan.items) {
|
|
16707
|
-
queriesByAlias.
|
|
17136
|
+
if (!queriesByAlias.has(item.targetAlias)) {
|
|
17137
|
+
queriesByAlias.set(item.targetAlias, serializeJoinPushdownItem(item, sources));
|
|
17138
|
+
}
|
|
16708
17139
|
conditionsByAlias.set(item.targetAlias, item.predicate);
|
|
16709
17140
|
}
|
|
16710
17141
|
const mainAlias = stmt.from.alias;
|
|
@@ -17346,15 +17777,24 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17346
17777
|
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
17347
17778
|
);
|
|
17348
17779
|
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
17349
|
-
const
|
|
17780
|
+
const preboundJoinPlan = boundJoinRuntimePlans.get(stmt);
|
|
17781
|
+
if (preboundJoinPlan && !isJoinServerFunctionFetchPlan(preboundJoinPlan.joinPlan)) {
|
|
17782
|
+
throw new Error("InternalError: JOIN server-function fetch binding changed before records API.");
|
|
17783
|
+
}
|
|
17784
|
+
const pushdownPlan = preboundJoinPlan ?? buildRuntimeJoinPushdownPlan(stmt, pushdownMeta) ?? buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
17350
17785
|
validateKlikePushdownPlan(pushdownPlan);
|
|
17786
|
+
const runtimeJoinPlan = "joinPlan" in pushdownPlan ? pushdownPlan : null;
|
|
17787
|
+
const boundServerFunctionPlan = runtimeJoinPlan && isJoinServerFunctionFetchPlan(runtimeJoinPlan.joinPlan) ? runtimeJoinPlan : null;
|
|
17351
17788
|
const fetchClient = client;
|
|
17352
17789
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
17353
17790
|
const tableConditions = pushdownPlan.joinConditions;
|
|
17354
17791
|
if (prefilterPlan && allowOriginalWherePushdown) {
|
|
17355
17792
|
throw new Error("internal error: relative-date prefilter must disable original WHERE pushdown.");
|
|
17356
17793
|
}
|
|
17357
|
-
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : mainPushDown;
|
|
17794
|
+
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : boundServerFunctionPlan ? null : mainPushDown;
|
|
17795
|
+
const mainBoundQuery = boundServerFunctionPlan?.queriesByAlias.get(
|
|
17796
|
+
stmt.from.alias
|
|
17797
|
+
) ?? "";
|
|
17358
17798
|
const scalarCache = await resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
17359
17799
|
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
17360
17800
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
@@ -17367,8 +17807,9 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17367
17807
|
options.onLimitReached ?? "error",
|
|
17368
17808
|
warnings,
|
|
17369
17809
|
mainFetchCondition,
|
|
17370
|
-
allowOriginalWherePushdown,
|
|
17371
|
-
plainGroupByPlan
|
|
17810
|
+
boundServerFunctionPlan ? false : allowOriginalWherePushdown,
|
|
17811
|
+
plainGroupByPlan,
|
|
17812
|
+
mainBoundQuery
|
|
17372
17813
|
);
|
|
17373
17814
|
const parallelJoins = [];
|
|
17374
17815
|
const onOptJoins = [];
|
|
@@ -17377,8 +17818,9 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17377
17818
|
parallelJoins.push({ join: join2, promise: Promise.resolve([]) });
|
|
17378
17819
|
continue;
|
|
17379
17820
|
}
|
|
17380
|
-
const
|
|
17381
|
-
|
|
17821
|
+
const boundQuery = join2.table.alias ? boundServerFunctionPlan?.queriesByAlias.get(join2.table.alias) ?? "" : "";
|
|
17822
|
+
const jCond = boundServerFunctionPlan ? null : join2.table.alias ? tableConditions.get(join2.table.alias) ?? null : null;
|
|
17823
|
+
if (jCond !== null || boundQuery !== "") {
|
|
17382
17824
|
parallelJoins.push({
|
|
17383
17825
|
join: join2,
|
|
17384
17826
|
promise: fetchTableRecordsForFullScan(
|
|
@@ -17392,7 +17834,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17392
17834
|
warnings,
|
|
17393
17835
|
jCond,
|
|
17394
17836
|
true,
|
|
17395
|
-
plainGroupByPlan
|
|
17837
|
+
plainGroupByPlan,
|
|
17838
|
+
boundQuery
|
|
17396
17839
|
)
|
|
17397
17840
|
});
|
|
17398
17841
|
} else {
|
|
@@ -17452,7 +17895,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17452
17895
|
havingFieldSemanticsResolver,
|
|
17453
17896
|
aggregateSortKindResolver,
|
|
17454
17897
|
appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
|
|
17455
|
-
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : {},
|
|
17898
|
+
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : boundServerFunctionPlan ? { residualWhere: boundServerFunctionPlan.joinPlan.residualWhere } : {},
|
|
17456
17899
|
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt),
|
|
17457
17900
|
plainGroupByPlan
|
|
17458
17901
|
});
|
|
@@ -21160,8 +21603,11 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
21160
21603
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
21161
21604
|
}
|
|
21162
21605
|
capabilities.set(select, capability);
|
|
21163
|
-
const
|
|
21164
|
-
const joinPushdownPlan = buildRuntimeJoinPushdownPlan(
|
|
21606
|
+
const preboundJoinPushdownPlan = boundJoinRuntimePlans.get(select);
|
|
21607
|
+
const joinPushdownPlan = preboundJoinPushdownPlan ?? buildRuntimeJoinPushdownPlan(
|
|
21608
|
+
select,
|
|
21609
|
+
await loadTypedPushdownMeta(select, tracedClient, cacheContext)
|
|
21610
|
+
);
|
|
21165
21611
|
if (joinPushdownPlan) explainJoinPushdownPlans.set(select, joinPushdownPlan);
|
|
21166
21612
|
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
21167
21613
|
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
@@ -21399,9 +21845,15 @@ function relativeDateExplainLines(plan) {
|
|
|
21399
21845
|
if (!plan.hasServerOnlyWhereFunction) return [];
|
|
21400
21846
|
if (!plan.allowed && plan.rejection) {
|
|
21401
21847
|
const label = isRelativeDateFunctionName(plan.rejection.functionName) ? "relative date function" : "kintone function";
|
|
21848
|
+
const rejectedNode2 = plan.nodes[plan.nodes.length - 1];
|
|
21849
|
+
const detail = rejectedNode2?.capability?.reasons.find(
|
|
21850
|
+
(reason) => reason.functionName === plan.rejection.functionName
|
|
21851
|
+
);
|
|
21852
|
+
const target = rejectedNode2 ? findServerFunctionExplainTarget(rejectedNode2.source, plan.rejection.functionName) : null;
|
|
21402
21853
|
return [
|
|
21403
21854
|
` ${label}: ${plan.rejection.functionName}`,
|
|
21404
21855
|
" plan status: rejected",
|
|
21856
|
+
` target alias / field: ${target?.alias ?? "(unknown)"} / ${detail?.field ?? target?.field ?? "(unknown)"}`,
|
|
21405
21857
|
` reason: ${plan.rejection.reasonCodes.join(", ")}`,
|
|
21406
21858
|
" client evaluation: forbidden",
|
|
21407
21859
|
" records/cursor/mutation API during EXPLAIN: none"
|
|
@@ -21409,6 +21861,41 @@ function relativeDateExplainLines(plan) {
|
|
|
21409
21861
|
}
|
|
21410
21862
|
const lines = [];
|
|
21411
21863
|
for (const node of plan.nodes) {
|
|
21864
|
+
const joinServerFunctionPlan = node.joinServerFunctionPlan;
|
|
21865
|
+
if (node.allowed && node.allowForm === "JOIN_SERVER_FUNCTION_EXACT" && joinServerFunctionPlan) {
|
|
21866
|
+
const variant = node.joinServerFunctionVariant === "WHOLE_WHERE_EXACT" ? "whole-WHERE" : "leaf";
|
|
21867
|
+
lines.push(
|
|
21868
|
+
` allow form: JOIN_SERVER_FUNCTION_EXACT (${variant})`
|
|
21869
|
+
);
|
|
21870
|
+
for (const consumption of joinServerFunctionPlan.serverFunctionConsumptions) {
|
|
21871
|
+
for (const leaf of consumption.functionLeaves) {
|
|
21872
|
+
const functionName = serverFunctionNameOfExplainLeaf(leaf);
|
|
21873
|
+
const field = leaf.left.type === "FIELD" ? leaf.left.field : "(unknown)";
|
|
21874
|
+
lines.push(
|
|
21875
|
+
` ${serverFunctionLabel(functionName)}: ${functionName}`,
|
|
21876
|
+
` ${serverFunctionEvaluationLabel(functionName)}: kintone server exact JOIN prefilter`,
|
|
21877
|
+
` target alias / APP: ${consumption.targetAlias} / APP${consumption.appId}`,
|
|
21878
|
+
` field: ${field}`,
|
|
21879
|
+
` function leaf relation: ${consumption.relation}`,
|
|
21880
|
+
` consumption: ${consumption.consumption}`
|
|
21881
|
+
);
|
|
21882
|
+
}
|
|
21883
|
+
}
|
|
21884
|
+
lines.push(
|
|
21885
|
+
` client residual: ${joinServerFunctionPlan.residualWhere === null ? "(none)" : renderRelativeDateResidualWhere(joinServerFunctionPlan.residualWhere)}`
|
|
21886
|
+
);
|
|
21887
|
+
if (joinServerFunctionPlan.adoptedServerFunctionOccurrences.some(
|
|
21888
|
+
isRelativeDateFunctionName
|
|
21889
|
+
)) {
|
|
21890
|
+
lines.push(" relative date client evaluations: 0");
|
|
21891
|
+
}
|
|
21892
|
+
if (joinServerFunctionPlan.adoptedServerFunctionOccurrences.some(
|
|
21893
|
+
(name) => !isRelativeDateFunctionName(name)
|
|
21894
|
+
)) {
|
|
21895
|
+
lines.push(" kintone function client evaluations: 0");
|
|
21896
|
+
}
|
|
21897
|
+
continue;
|
|
21898
|
+
}
|
|
21412
21899
|
const fullScanExactPlan = node.fullScanExactPlan;
|
|
21413
21900
|
if (node.allowed && node.allowForm === "FULL_SCAN_EXACT" && fullScanExactPlan) {
|
|
21414
21901
|
for (const leaf of fullScanExactPlan.prefilterPlan.exactRelativeLeaves) {
|
|
@@ -21493,6 +21980,36 @@ function relativeDateExplainLines(plan) {
|
|
|
21493
21980
|
}
|
|
21494
21981
|
return lines;
|
|
21495
21982
|
}
|
|
21983
|
+
function findServerFunctionExplainTarget(source, functionName) {
|
|
21984
|
+
const where = "where" in source ? source.where : null;
|
|
21985
|
+
let found = null;
|
|
21986
|
+
const visit = (value) => {
|
|
21987
|
+
if (found || value === null || typeof value !== "object") return;
|
|
21988
|
+
if (Array.isArray(value)) {
|
|
21989
|
+
value.forEach(visit);
|
|
21990
|
+
return;
|
|
21991
|
+
}
|
|
21992
|
+
const node = value;
|
|
21993
|
+
if (node["type"] === "BINARY") {
|
|
21994
|
+
const left = node["left"];
|
|
21995
|
+
const right = node["right"];
|
|
21996
|
+
const directlyMatches = right?.["type"] === "KINTONE_FUNC" && right["name"] === functionName;
|
|
21997
|
+
const listMatches = right?.["type"] === "IN_LIST" && Array.isArray(right["values"]) && right["values"].some(
|
|
21998
|
+
(entry) => entry !== null && typeof entry === "object" && entry["type"] === "KINTONE_FUNC" && entry["name"] === functionName
|
|
21999
|
+
);
|
|
22000
|
+
if ((directlyMatches || listMatches) && left?.["type"] === "FIELD" && typeof left["field"] === "string") {
|
|
22001
|
+
found = {
|
|
22002
|
+
alias: typeof left["tableAlias"] === "string" ? left["tableAlias"] : null,
|
|
22003
|
+
field: left["field"]
|
|
22004
|
+
};
|
|
22005
|
+
return;
|
|
22006
|
+
}
|
|
22007
|
+
}
|
|
22008
|
+
Object.values(node).forEach(visit);
|
|
22009
|
+
};
|
|
22010
|
+
visit(where);
|
|
22011
|
+
return found;
|
|
22012
|
+
}
|
|
21496
22013
|
function serverFunctionLabel(functionName) {
|
|
21497
22014
|
return isRelativeDateFunctionName(functionName) ? "relative date function" : "kintone function";
|
|
21498
22015
|
}
|
|
@@ -21910,10 +22427,22 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21910
22427
|
lines.push(" join pushdown plan: applied (runtime metadata resolved)");
|
|
21911
22428
|
lines.push(" runtime plan timing: variables/subqueries resolved -> metadata resolved -> immutable plan");
|
|
21912
22429
|
lines.push(" EXPLAIN unresolved subqueries: not applied (records API is not called)");
|
|
21913
|
-
|
|
22430
|
+
if (runtimeJoinPlan.joinPlan.serverFunctionCandidate) {
|
|
22431
|
+
lines.push(
|
|
22432
|
+
` allow form: JOIN_SERVER_FUNCTION_EXACT (${runtimeJoinPlan.joinPlan.serverFunctionCandidate.variant === "WHOLE_WHERE_EXACT" ? "whole-WHERE" : "leaf"})`
|
|
22433
|
+
);
|
|
22434
|
+
}
|
|
22435
|
+
lines.push(
|
|
22436
|
+
` client residual: ${runtimeJoinPlan.joinPlan.residualWhere === null ? "(none)" : renderRelativeDateResidualWhere(runtimeJoinPlan.joinPlan.residualWhere)}`
|
|
22437
|
+
);
|
|
21914
22438
|
lines.push(` KLIKE applied nodes: ${runtimeJoinPlan.joinPlan.appliedKlikes.size}`);
|
|
21915
|
-
|
|
21916
|
-
|
|
22439
|
+
lines.push(
|
|
22440
|
+
` KLIKE unapplied nodes: ${runtimeJoinPlan.joinPlan.allKlikes.length - runtimeJoinPlan.joinPlan.appliedKlikes.size}`
|
|
22441
|
+
);
|
|
22442
|
+
if (runtimeJoinPlan.joinPlan.residualWhere !== null) {
|
|
22443
|
+
for (const rejection of runtimeJoinPlan.joinPlan.rejections) {
|
|
22444
|
+
lines.push(` join pushdown not applied: ${rejection.reason}`);
|
|
22445
|
+
}
|
|
21917
22446
|
}
|
|
21918
22447
|
} else {
|
|
21919
22448
|
const reason = stmt.joins.some((join2) => join2.type !== "INNER") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
|
|
@@ -21928,15 +22457,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21928
22457
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
21929
22458
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
21930
22459
|
const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
|
|
21931
|
-
const
|
|
22460
|
+
const mainBoundQuery = stmt.from.alias ? runtimeJoinPlan?.queriesByAlias.get(stmt.from.alias) : void 0;
|
|
22461
|
+
const mainQ = mainBoundQuery || (mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)");
|
|
21932
22462
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
21933
22463
|
lines.push(` kintone query: ${mainQ}`);
|
|
21934
22464
|
const mainJoinItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21935
22465
|
(item) => item.targetAlias === stmt.from.alias
|
|
21936
22466
|
);
|
|
21937
|
-
|
|
21938
|
-
|
|
21939
|
-
|
|
22467
|
+
const mainFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
|
|
22468
|
+
(consumption) => consumption.targetAlias === stmt.from.alias
|
|
22469
|
+
);
|
|
22470
|
+
if (mainJoinItem || mainFunctionConsumption) {
|
|
22471
|
+
lines.push(` pushdown applied: ${mainBoundQuery}`);
|
|
22472
|
+
lines.push(` relation: ${mainJoinItem?.relation ?? "exact"}`);
|
|
21940
22473
|
} else if (!runtimeJoinPlan && mainCandidate !== null) {
|
|
21941
22474
|
lines.push(` pushdown candidate: ${whereToKintone(mainCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21942
22475
|
}
|
|
@@ -21947,15 +22480,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21947
22480
|
const joinType = join2.type === "INNER" ? "JOIN" : `${join2.type} JOIN`;
|
|
21948
22481
|
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
21949
22482
|
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
21950
|
-
const
|
|
22483
|
+
const joinBoundQuery = join2.table.alias ? runtimeJoinPlan?.queriesByAlias.get(join2.table.alias) : void 0;
|
|
22484
|
+
const joinQ = joinBoundQuery || (joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)");
|
|
21951
22485
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
21952
22486
|
lines.push(` kintone query: ${joinQ}`);
|
|
21953
22487
|
const joinPlanItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21954
22488
|
(item) => item.targetAlias === join2.table.alias
|
|
21955
22489
|
);
|
|
21956
|
-
|
|
21957
|
-
|
|
21958
|
-
|
|
22490
|
+
const joinFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
|
|
22491
|
+
(consumption) => consumption.targetAlias === join2.table.alias
|
|
22492
|
+
);
|
|
22493
|
+
if (joinPlanItem || joinFunctionConsumption) {
|
|
22494
|
+
lines.push(` pushdown applied: ${joinBoundQuery}`);
|
|
22495
|
+
lines.push(` relation: ${joinPlanItem?.relation ?? "exact"}`);
|
|
21959
22496
|
} else if (!runtimeJoinPlan && joinCandidate !== null) {
|
|
21960
22497
|
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21961
22498
|
}
|