@rex0220/kintone-sql-tools 3.20.0 → 3.22.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 +570 -31
- package/dist-engine/index.cjs +10 -10
- package/dist-engine/index.mjs +10 -10
- package/dist-engine/ksql-engine.umd.js +10 -10
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +49 -10
- package/dist-engine/meta/esm.json +49 -10
- package/dist-engine/meta/umd.json +49 -10
- package/dist-engine/publicTypes.d.ts +6 -0
- package/dist-mcp/ksql-mcp.js +573 -33
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +2 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -11929,7 +11929,8 @@ function runFullScan(input) {
|
|
|
11929
11929
|
});
|
|
11930
11930
|
knownColumns = mergeKnownColumns(knownColumns, rightColumns, rows);
|
|
11931
11931
|
}
|
|
11932
|
-
|
|
11932
|
+
const filterWhere = input.residualWhere !== void 0 ? input.residualWhere : stmt.where;
|
|
11933
|
+
rows = applyFilter(rows, filterWhere, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
|
|
11933
11934
|
const grouping = normalizeGroupingSpec(stmt);
|
|
11934
11935
|
if (grouping.type === "GROUPING_SETS") {
|
|
11935
11936
|
if (!resolvedGroupingSpec) {
|
|
@@ -12452,7 +12453,7 @@ function nestedSelects(node, root) {
|
|
|
12452
12453
|
visit(node);
|
|
12453
12454
|
return found;
|
|
12454
12455
|
}
|
|
12455
|
-
function collectSelect(select, path, candidates, forceForbidden) {
|
|
12456
|
+
function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = true) {
|
|
12456
12457
|
const functionNames = relativeDateFunctionNamesInWhere(select.where);
|
|
12457
12458
|
if (functionNames.length > 0) {
|
|
12458
12459
|
candidates.push({
|
|
@@ -12460,11 +12461,18 @@ function collectSelect(select, path, candidates, forceForbidden) {
|
|
|
12460
12461
|
source: select,
|
|
12461
12462
|
where: select.where,
|
|
12462
12463
|
functionNames,
|
|
12463
|
-
path
|
|
12464
|
+
path,
|
|
12465
|
+
allowPhase2Prefilter: allowPhase2
|
|
12464
12466
|
});
|
|
12465
12467
|
}
|
|
12466
12468
|
nestedSelects(select, select).forEach(
|
|
12467
|
-
(nested, index) => collectSelect(
|
|
12469
|
+
(nested, index) => collectSelect(
|
|
12470
|
+
nested,
|
|
12471
|
+
`${path}.select-source[${index}]`,
|
|
12472
|
+
candidates,
|
|
12473
|
+
forceForbidden,
|
|
12474
|
+
allowPhase2
|
|
12475
|
+
)
|
|
12468
12476
|
);
|
|
12469
12477
|
}
|
|
12470
12478
|
function collectUnion(union, path, candidates, forceForbidden) {
|
|
@@ -12552,19 +12560,34 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12552
12560
|
}
|
|
12553
12561
|
}
|
|
12554
12562
|
nestedSelects(statement, statement).forEach(
|
|
12555
|
-
(select, index) => collectSelect(
|
|
12563
|
+
(select, index) => collectSelect(
|
|
12564
|
+
select,
|
|
12565
|
+
`${path}.select-source[${index}]`,
|
|
12566
|
+
candidates,
|
|
12567
|
+
forceForbidden,
|
|
12568
|
+
false
|
|
12569
|
+
)
|
|
12556
12570
|
);
|
|
12557
12571
|
return;
|
|
12558
12572
|
}
|
|
12559
12573
|
default:
|
|
12560
12574
|
nestedSelects(statement, statement).forEach(
|
|
12561
|
-
(select, index) => collectSelect(
|
|
12575
|
+
(select, index) => collectSelect(
|
|
12576
|
+
select,
|
|
12577
|
+
`${path}.select-source[${index}]`,
|
|
12578
|
+
candidates,
|
|
12579
|
+
forceForbidden,
|
|
12580
|
+
false
|
|
12581
|
+
)
|
|
12562
12582
|
);
|
|
12563
12583
|
}
|
|
12564
12584
|
}
|
|
12565
12585
|
function serializationContainsFunctions(query, names) {
|
|
12566
12586
|
return names.every((name) => new RegExp(`\\b${name}\\s*\\(`).test(query));
|
|
12567
12587
|
}
|
|
12588
|
+
function allowRelativeDatePrefilterPlan(select, decomposition) {
|
|
12589
|
+
return decomposition.eligible === true && resolveSelectMode(select) === "FULL_SCAN" && select.orderMode !== "KINTONE_NATIVE" && select.from.cteName === null && !select.from.subtableCode && select.joins.length === 0;
|
|
12590
|
+
}
|
|
12568
12591
|
function rejectedNode(candidate) {
|
|
12569
12592
|
return {
|
|
12570
12593
|
kind: candidate.kind,
|
|
@@ -12621,7 +12644,17 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12621
12644
|
} catch {
|
|
12622
12645
|
restQuery2 = "";
|
|
12623
12646
|
}
|
|
12624
|
-
|
|
12647
|
+
let allowed2 = physicalTopLevel && selectMode === "SIMPLE" && (select.orderBy.length === 0 || select.orderMode === "KINTONE_NATIVE") && capability2.capability === "EXACT_PUSHDOWN" && serializationContainsFunctions(restQuery2, candidate.functionNames);
|
|
12648
|
+
let prefilterPlan;
|
|
12649
|
+
let phase2PrefilterEligible;
|
|
12650
|
+
if (!allowed2 && candidate.allowPhase2Prefilter !== false && capability2.capability === "SUPERSET_PREFILTER" && resolver.prefilterDecomposition) {
|
|
12651
|
+
const decomposition = await resolver.prefilterDecomposition(select);
|
|
12652
|
+
if (decomposition?.eligible === true && allowRelativeDatePrefilterPlan(select, decomposition)) {
|
|
12653
|
+
prefilterPlan = decomposition.plan;
|
|
12654
|
+
phase2PrefilterEligible = true;
|
|
12655
|
+
allowed2 = true;
|
|
12656
|
+
}
|
|
12657
|
+
}
|
|
12625
12658
|
const node2 = {
|
|
12626
12659
|
kind: candidate.kind,
|
|
12627
12660
|
source: candidate.source,
|
|
@@ -12630,6 +12663,7 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12630
12663
|
selectMode,
|
|
12631
12664
|
capability: capability2,
|
|
12632
12665
|
restQuery: restQuery2,
|
|
12666
|
+
...prefilterPlan ? { prefilterPlan, phase2PrefilterEligible } : {},
|
|
12633
12667
|
clientWhereEvaluation: !allowed2,
|
|
12634
12668
|
allowed: allowed2
|
|
12635
12669
|
};
|
|
@@ -12690,6 +12724,312 @@ function assertRelativeDatePushdownPlan(plan) {
|
|
|
12690
12724
|
}
|
|
12691
12725
|
}
|
|
12692
12726
|
|
|
12727
|
+
// src/core/optimization/relativeDatePrefilterPlan.ts
|
|
12728
|
+
function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
12729
|
+
const capability = classifyWhereCapability(stmt.where, resolveField2);
|
|
12730
|
+
const reject = (reasonCodes, disposition = "INELIGIBLE") => ({
|
|
12731
|
+
eligible: false,
|
|
12732
|
+
disposition,
|
|
12733
|
+
reasonCodes,
|
|
12734
|
+
capability: capability.capability,
|
|
12735
|
+
reasons: capability.reasons
|
|
12736
|
+
});
|
|
12737
|
+
if (stmt.where === null) return reject(["NO_WHERE"]);
|
|
12738
|
+
if (stmt.from.subtableCode) return reject(["SUBTABLE_UNSUPPORTED"]);
|
|
12739
|
+
if (stmt.joins.length > 0) return reject(["JOIN_UNSUPPORTED"]);
|
|
12740
|
+
if (stmt.from.cteName !== null || stmt.from.appId <= 0) {
|
|
12741
|
+
return reject(["NOT_DIRECT_PHYSICAL_APP"]);
|
|
12742
|
+
}
|
|
12743
|
+
const occurrences = collectRelativeOccurrences(stmt.where);
|
|
12744
|
+
if (occurrences.length === 0) return reject(["NO_RELATIVE_DATE"]);
|
|
12745
|
+
const spine = collectRelativeLeavesOnAndSpine(stmt.where, resolveField2);
|
|
12746
|
+
if (!spine.ok) return reject(spine.reasonCodes);
|
|
12747
|
+
if (!sameRelativeMultiset(occurrences, spine.leaves)) {
|
|
12748
|
+
return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
12749
|
+
}
|
|
12750
|
+
if (capability.capability === "EXACT_PUSHDOWN") {
|
|
12751
|
+
return reject(["DEFER_TO_PHASE1"], "DEFER_PHASE1");
|
|
12752
|
+
}
|
|
12753
|
+
if (capability.capability !== "SUPERSET_PREFILTER") {
|
|
12754
|
+
return reject(["CAPABILITY_NOT_SUPERSET_PREFILTER"]);
|
|
12755
|
+
}
|
|
12756
|
+
const fieldMetadata = collectFieldMetadata(stmt.where, resolveField2);
|
|
12757
|
+
const safePlan = buildSingleTableKlikePushdownPlan(stmt.where, {
|
|
12758
|
+
tableAlias: stmt.from.alias ?? void 0,
|
|
12759
|
+
allowUnqualifiedFields: true,
|
|
12760
|
+
allowKlike: true,
|
|
12761
|
+
fieldTypes: fieldMetadata.fieldTypes,
|
|
12762
|
+
fieldOptions: fieldMetadata.fieldOptions
|
|
12763
|
+
});
|
|
12764
|
+
const serialize = testSeam.serialize ?? whereToKintone;
|
|
12765
|
+
const containsFunctions = testSeam.containsFunctions ?? serializationContainsFunctions;
|
|
12766
|
+
const confirmedLeaves = [];
|
|
12767
|
+
for (const leaf of spine.leaves) {
|
|
12768
|
+
const name = relativeNameOf(leaf);
|
|
12769
|
+
if (name === null) return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
12770
|
+
let query;
|
|
12771
|
+
try {
|
|
12772
|
+
query = serialize(leaf);
|
|
12773
|
+
} catch {
|
|
12774
|
+
return reject(["PREFILTER_SERIALIZATION_FAILED"]);
|
|
12775
|
+
}
|
|
12776
|
+
if (!containsFunctions(query, [name])) {
|
|
12777
|
+
return reject(["PREFILTER_FUNCTION_MISSING"]);
|
|
12778
|
+
}
|
|
12779
|
+
confirmedLeaves.push(leaf);
|
|
12780
|
+
}
|
|
12781
|
+
const safeLeaves = collectBinaryIdentities(safePlan.condition);
|
|
12782
|
+
const adoptedLeaves = new Set(confirmedLeaves);
|
|
12783
|
+
const prefilterWhere = selectPrefilterInOriginalOrder(
|
|
12784
|
+
stmt.where,
|
|
12785
|
+
adoptedLeaves,
|
|
12786
|
+
safeLeaves
|
|
12787
|
+
);
|
|
12788
|
+
if (prefilterWhere === null) {
|
|
12789
|
+
return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
12790
|
+
}
|
|
12791
|
+
let prefilterQuery;
|
|
12792
|
+
try {
|
|
12793
|
+
prefilterQuery = serialize(prefilterWhere);
|
|
12794
|
+
} catch {
|
|
12795
|
+
return reject(["PREFILTER_SERIALIZATION_FAILED"]);
|
|
12796
|
+
}
|
|
12797
|
+
const expectedNames = confirmedLeaves.map((leaf) => relativeNameOf(leaf));
|
|
12798
|
+
if (!containsFunctions(prefilterQuery, [...new Set(expectedNames)]) || !serializedMultisetContains(prefilterQuery, expectedNames)) {
|
|
12799
|
+
return reject(["PREFILTER_FUNCTION_MISSING"]);
|
|
12800
|
+
}
|
|
12801
|
+
const residualWhere = testSeam.rewriteResidual ? testSeam.rewriteResidual(stmt.where, adoptedLeaves) : replaceAdoptedLeaves(stmt.where, adoptedLeaves);
|
|
12802
|
+
if (residualWhere !== null && collectRelativeOccurrences(residualWhere).length > 0) {
|
|
12803
|
+
return reject(["RESIDUAL_RELATIVE_DATE_REMAINED"]);
|
|
12804
|
+
}
|
|
12805
|
+
if (residualWhere === null) {
|
|
12806
|
+
return reject(["DEFER_TO_PHASE1"], "DEFER_PHASE1");
|
|
12807
|
+
}
|
|
12808
|
+
const relativeFunctionNames = /* @__PURE__ */ new Set();
|
|
12809
|
+
for (const name of expectedNames) relativeFunctionNames.add(name);
|
|
12810
|
+
return {
|
|
12811
|
+
eligible: true,
|
|
12812
|
+
plan: {
|
|
12813
|
+
prefilterWhere,
|
|
12814
|
+
residualWhere,
|
|
12815
|
+
exactRelativeLeaves: confirmedLeaves,
|
|
12816
|
+
relativeFunctionNames,
|
|
12817
|
+
appliedKlikes: safePlan.appliedKlikes,
|
|
12818
|
+
capability: capability.capability,
|
|
12819
|
+
reasons: capability.reasons
|
|
12820
|
+
}
|
|
12821
|
+
};
|
|
12822
|
+
}
|
|
12823
|
+
function collectRelativeLeavesOnAndSpine(where, resolveField2) {
|
|
12824
|
+
const leaves = [];
|
|
12825
|
+
let failure = null;
|
|
12826
|
+
const visit = (node) => {
|
|
12827
|
+
if (failure !== null) return;
|
|
12828
|
+
switch (node.type) {
|
|
12829
|
+
case "GROUP":
|
|
12830
|
+
visit(node.expr);
|
|
12831
|
+
return;
|
|
12832
|
+
case "LOGICAL":
|
|
12833
|
+
if (node.op === "AND") {
|
|
12834
|
+
visit(node.left);
|
|
12835
|
+
visit(node.right);
|
|
12836
|
+
return;
|
|
12837
|
+
}
|
|
12838
|
+
if (collectRelativeOccurrences(node).length > 0) {
|
|
12839
|
+
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
12840
|
+
}
|
|
12841
|
+
return;
|
|
12842
|
+
case "NOT":
|
|
12843
|
+
if (collectRelativeOccurrences(node).length > 0) {
|
|
12844
|
+
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
12845
|
+
}
|
|
12846
|
+
return;
|
|
12847
|
+
case "BINARY": {
|
|
12848
|
+
const name = relativeNameOf(node);
|
|
12849
|
+
if (name === null) return;
|
|
12850
|
+
const result = classifyRelativeDateBinary(
|
|
12851
|
+
node.op,
|
|
12852
|
+
node.left,
|
|
12853
|
+
node.right,
|
|
12854
|
+
resolveField2
|
|
12855
|
+
);
|
|
12856
|
+
if (result.capability !== "EXACT_PUSHDOWN") {
|
|
12857
|
+
failure = "RELATIVE_DATE_LEAF_NOT_EXACT";
|
|
12858
|
+
return;
|
|
12859
|
+
}
|
|
12860
|
+
leaves.push(node);
|
|
12861
|
+
return;
|
|
12862
|
+
}
|
|
12863
|
+
case "EXISTS":
|
|
12864
|
+
if (collectRelativeOccurrences(node).length > 0) {
|
|
12865
|
+
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
12866
|
+
}
|
|
12867
|
+
return;
|
|
12868
|
+
case "NULL_CHECK":
|
|
12869
|
+
case "BOOLEAN":
|
|
12870
|
+
return;
|
|
12871
|
+
}
|
|
12872
|
+
};
|
|
12873
|
+
visit(where);
|
|
12874
|
+
return failure === null ? { ok: true, leaves } : { ok: false, reasonCodes: [failure] };
|
|
12875
|
+
}
|
|
12876
|
+
function relativeNameOf(leaf) {
|
|
12877
|
+
return leaf.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(leaf.right.name) ? leaf.right.name : null;
|
|
12878
|
+
}
|
|
12879
|
+
function collectRelativeOccurrences(where) {
|
|
12880
|
+
const found = [];
|
|
12881
|
+
const visitWhere = (node) => {
|
|
12882
|
+
if (node.type === "BINARY" && relativeNameOf(node) !== null) {
|
|
12883
|
+
found.push(node);
|
|
12884
|
+
return;
|
|
12885
|
+
}
|
|
12886
|
+
switch (node.type) {
|
|
12887
|
+
case "LOGICAL":
|
|
12888
|
+
visitWhere(node.left);
|
|
12889
|
+
visitWhere(node.right);
|
|
12890
|
+
return;
|
|
12891
|
+
case "NOT":
|
|
12892
|
+
case "GROUP":
|
|
12893
|
+
visitWhere(node.expr);
|
|
12894
|
+
return;
|
|
12895
|
+
case "EXISTS":
|
|
12896
|
+
if (node.query.where !== null) visitWhere(node.query.where);
|
|
12897
|
+
return;
|
|
12898
|
+
case "BINARY":
|
|
12899
|
+
case "NULL_CHECK":
|
|
12900
|
+
case "BOOLEAN":
|
|
12901
|
+
return;
|
|
12902
|
+
}
|
|
12903
|
+
};
|
|
12904
|
+
visitWhere(where);
|
|
12905
|
+
return found;
|
|
12906
|
+
}
|
|
12907
|
+
function sameRelativeMultiset(occurrences, candidates) {
|
|
12908
|
+
if (occurrences.length !== candidates.length) return false;
|
|
12909
|
+
const remaining = new Set(candidates);
|
|
12910
|
+
for (const occurrence of occurrences) {
|
|
12911
|
+
if (!remaining.delete(occurrence)) return false;
|
|
12912
|
+
}
|
|
12913
|
+
return remaining.size === 0;
|
|
12914
|
+
}
|
|
12915
|
+
function collectBinaryIdentities(where) {
|
|
12916
|
+
const found = /* @__PURE__ */ new Set();
|
|
12917
|
+
const visit = (node) => {
|
|
12918
|
+
switch (node.type) {
|
|
12919
|
+
case "BINARY":
|
|
12920
|
+
found.add(node);
|
|
12921
|
+
return;
|
|
12922
|
+
case "LOGICAL":
|
|
12923
|
+
visit(node.left);
|
|
12924
|
+
visit(node.right);
|
|
12925
|
+
return;
|
|
12926
|
+
case "NOT":
|
|
12927
|
+
case "GROUP":
|
|
12928
|
+
visit(node.expr);
|
|
12929
|
+
return;
|
|
12930
|
+
case "NULL_CHECK":
|
|
12931
|
+
case "EXISTS":
|
|
12932
|
+
case "BOOLEAN":
|
|
12933
|
+
return;
|
|
12934
|
+
}
|
|
12935
|
+
};
|
|
12936
|
+
if (where !== null) visit(where);
|
|
12937
|
+
return found;
|
|
12938
|
+
}
|
|
12939
|
+
function selectPrefilterInOriginalOrder(where, relativeLeaves, safeLeaves) {
|
|
12940
|
+
switch (where.type) {
|
|
12941
|
+
case "BINARY":
|
|
12942
|
+
return relativeLeaves.has(where) || safeLeaves.has(where) ? where : null;
|
|
12943
|
+
case "LOGICAL":
|
|
12944
|
+
if (where.op !== "AND") return null;
|
|
12945
|
+
{
|
|
12946
|
+
const left = selectPrefilterInOriginalOrder(where.left, relativeLeaves, safeLeaves);
|
|
12947
|
+
const right = selectPrefilterInOriginalOrder(where.right, relativeLeaves, safeLeaves);
|
|
12948
|
+
if (left !== null && right !== null) return { ...where, left, right };
|
|
12949
|
+
return left ?? right;
|
|
12950
|
+
}
|
|
12951
|
+
case "GROUP": {
|
|
12952
|
+
const expr = selectPrefilterInOriginalOrder(where.expr, relativeLeaves, safeLeaves);
|
|
12953
|
+
return expr === null ? null : { ...where, expr };
|
|
12954
|
+
}
|
|
12955
|
+
case "NULL_CHECK":
|
|
12956
|
+
case "NOT":
|
|
12957
|
+
case "EXISTS":
|
|
12958
|
+
case "BOOLEAN":
|
|
12959
|
+
return null;
|
|
12960
|
+
}
|
|
12961
|
+
}
|
|
12962
|
+
var TRUE_PREDICATE = { type: "BOOLEAN", value: true };
|
|
12963
|
+
function replaceAdoptedLeaves(where, adoptedLeaves) {
|
|
12964
|
+
if (where.type === "BINARY" && adoptedLeaves.has(where)) return TRUE_PREDICATE;
|
|
12965
|
+
switch (where.type) {
|
|
12966
|
+
case "LOGICAL": {
|
|
12967
|
+
if (where.op !== "AND") return where;
|
|
12968
|
+
const left = replaceAdoptedLeaves(where.left, adoptedLeaves) ?? TRUE_PREDICATE;
|
|
12969
|
+
const right = replaceAdoptedLeaves(where.right, adoptedLeaves) ?? TRUE_PREDICATE;
|
|
12970
|
+
if (isTrue(left)) return isTrue(right) ? null : right;
|
|
12971
|
+
if (isTrue(right)) return left;
|
|
12972
|
+
if (left === where.left && right === where.right) return where;
|
|
12973
|
+
return { ...where, left, right };
|
|
12974
|
+
}
|
|
12975
|
+
case "GROUP": {
|
|
12976
|
+
const expr = replaceAdoptedLeaves(where.expr, adoptedLeaves) ?? TRUE_PREDICATE;
|
|
12977
|
+
if (isTrue(expr)) return TRUE_PREDICATE;
|
|
12978
|
+
return expr === where.expr ? where : { ...where, expr };
|
|
12979
|
+
}
|
|
12980
|
+
case "BINARY":
|
|
12981
|
+
case "NULL_CHECK":
|
|
12982
|
+
case "NOT":
|
|
12983
|
+
case "EXISTS":
|
|
12984
|
+
case "BOOLEAN":
|
|
12985
|
+
return where;
|
|
12986
|
+
}
|
|
12987
|
+
}
|
|
12988
|
+
function isTrue(where) {
|
|
12989
|
+
return where.type === "BOOLEAN" && where.value;
|
|
12990
|
+
}
|
|
12991
|
+
function collectFieldMetadata(where, resolveField2) {
|
|
12992
|
+
const fields = /* @__PURE__ */ new Map();
|
|
12993
|
+
const visitValue = (value) => {
|
|
12994
|
+
if (value === null || typeof value !== "object") return;
|
|
12995
|
+
if (Array.isArray(value)) {
|
|
12996
|
+
value.forEach(visitValue);
|
|
12997
|
+
return;
|
|
12998
|
+
}
|
|
12999
|
+
const record = value;
|
|
13000
|
+
if (record["type"] === "SELECT") return;
|
|
13001
|
+
if (record["type"] === "FIELD" && typeof record["field"] === "string" && typeof record["tableAlias"] !== "undefined") {
|
|
13002
|
+
fields.set(record["field"], value);
|
|
13003
|
+
return;
|
|
13004
|
+
}
|
|
13005
|
+
Object.values(record).forEach(visitValue);
|
|
13006
|
+
};
|
|
13007
|
+
visitValue(where);
|
|
13008
|
+
const fieldTypes = /* @__PURE__ */ new Map();
|
|
13009
|
+
const fieldOptions = /* @__PURE__ */ new Map();
|
|
13010
|
+
for (const [fieldCode, field] of fields) {
|
|
13011
|
+
const semantics = resolveField2(field);
|
|
13012
|
+
if (!semantics) continue;
|
|
13013
|
+
fieldTypes.set(fieldCode, semantics.fieldType);
|
|
13014
|
+
if (semantics.optionOrder) {
|
|
13015
|
+
fieldOptions.set(fieldCode, new Set(semantics.optionOrder.keys()));
|
|
13016
|
+
}
|
|
13017
|
+
}
|
|
13018
|
+
return { fieldTypes, fieldOptions };
|
|
13019
|
+
}
|
|
13020
|
+
function serializedMultisetContains(query, expectedNames) {
|
|
13021
|
+
const expected = /* @__PURE__ */ new Map();
|
|
13022
|
+
for (const name of expectedNames) {
|
|
13023
|
+
if (!RELATIVE_DATE_FUNCTION_NAMES.has(name)) return false;
|
|
13024
|
+
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
13025
|
+
}
|
|
13026
|
+
for (const [name, count] of expected) {
|
|
13027
|
+
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
13028
|
+
if ((matches?.length ?? 0) < count) return false;
|
|
13029
|
+
}
|
|
13030
|
+
return true;
|
|
13031
|
+
}
|
|
13032
|
+
|
|
12693
13033
|
// src/import/sourceLoader.ts
|
|
12694
13034
|
var IMPORT_MAX_BYTES = 10 * 1024 * 1024;
|
|
12695
13035
|
var ImportSourceError = class extends Error {
|
|
@@ -13587,7 +13927,12 @@ async function execute(sql, client, options = {}) {
|
|
|
13587
13927
|
cacheContext
|
|
13588
13928
|
);
|
|
13589
13929
|
metrics.elapsedMs = Date.now() - startedAt;
|
|
13590
|
-
|
|
13930
|
+
const finalResult = { ...attachSearchAbortWarning(result, collector), metrics };
|
|
13931
|
+
if (result.type === "SELECT") {
|
|
13932
|
+
const columnMeta = materializedMetaBySelectResult.get(result);
|
|
13933
|
+
if (columnMeta) materializedMetaBySelectResult.set(finalResult, columnMeta);
|
|
13934
|
+
}
|
|
13935
|
+
return finalResult;
|
|
13591
13936
|
}
|
|
13592
13937
|
function createEmptyMetrics() {
|
|
13593
13938
|
return {
|
|
@@ -13753,7 +14098,16 @@ async function assertRelativeDateExecutionPlan(stmt, client, cacheContext) {
|
|
|
13753
14098
|
async function resolveRelativeDateExecutionPlan(stmt, client, cacheContext) {
|
|
13754
14099
|
return buildRelativeDatePushdownPlan(stmt, {
|
|
13755
14100
|
select: (select) => resolveSelectWhereCapability(select, client, cacheContext),
|
|
13756
|
-
dml: (dml) => resolveDmlWhereCapability(dml, client, cacheContext)
|
|
14101
|
+
dml: (dml) => resolveDmlWhereCapability(dml, client, cacheContext),
|
|
14102
|
+
prefilterDecomposition: async (select) => {
|
|
14103
|
+
if (select.where === null) return null;
|
|
14104
|
+
const resolver = await buildWhereFieldSemanticsResolver(
|
|
14105
|
+
select,
|
|
14106
|
+
client,
|
|
14107
|
+
cacheContext
|
|
14108
|
+
);
|
|
14109
|
+
return decomposeRelativeDatePrefilter(select, resolver);
|
|
14110
|
+
}
|
|
13757
14111
|
});
|
|
13758
14112
|
}
|
|
13759
14113
|
async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
@@ -13789,11 +14143,26 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
13789
14143
|
case "VALIDATE":
|
|
13790
14144
|
return executeExistingRecordValidation(stmt, client, options, cacheContext);
|
|
13791
14145
|
case "SELECT":
|
|
13792
|
-
return executeSelect(
|
|
14146
|
+
return executeSelect(
|
|
14147
|
+
stmt,
|
|
14148
|
+
client,
|
|
14149
|
+
options,
|
|
14150
|
+
cacheContext,
|
|
14151
|
+
void 0,
|
|
14152
|
+
options.captureColumnMeta === true,
|
|
14153
|
+
options.captureColumnMeta === true
|
|
14154
|
+
);
|
|
13793
14155
|
case "UNION":
|
|
13794
|
-
return executeUnion(
|
|
14156
|
+
return executeUnion(
|
|
14157
|
+
stmt,
|
|
14158
|
+
client,
|
|
14159
|
+
options,
|
|
14160
|
+
cacheContext,
|
|
14161
|
+
options.captureColumnMeta === true,
|
|
14162
|
+
options.captureColumnMeta === true
|
|
14163
|
+
);
|
|
13795
14164
|
case "WITH":
|
|
13796
|
-
return executeWith(stmt, client, options, cacheContext);
|
|
14165
|
+
return executeWith(stmt, client, options, cacheContext, void 0, options.captureColumnMeta === true);
|
|
13797
14166
|
case "INSERT":
|
|
13798
14167
|
return executeInsert(stmt, client, options, cacheContext);
|
|
13799
14168
|
case "INSERT_SELECT":
|
|
@@ -14847,13 +15216,16 @@ async function assertDmlWhereCapability(stmt, client, cacheContext) {
|
|
|
14847
15216
|
);
|
|
14848
15217
|
}
|
|
14849
15218
|
}
|
|
14850
|
-
async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
|
|
15219
|
+
async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false, forLibraryCapture = false) {
|
|
14851
15220
|
let result;
|
|
14852
15221
|
await validateSelectGroupingPlanning(stmt, client, cacheContext, cteCache);
|
|
14853
15222
|
if (isNoFromSelect(stmt)) {
|
|
14854
15223
|
result = executeNoFromSelect(stmt);
|
|
14855
15224
|
if (captureColumnMeta) {
|
|
14856
|
-
materializedMetaBySelectResult.set(
|
|
15225
|
+
materializedMetaBySelectResult.set(
|
|
15226
|
+
result,
|
|
15227
|
+
await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache, forLibraryCapture)
|
|
15228
|
+
);
|
|
14857
15229
|
}
|
|
14858
15230
|
return result;
|
|
14859
15231
|
}
|
|
@@ -14862,6 +15234,14 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
14862
15234
|
if (whereCapability.capability === "UNSUPPORTED") {
|
|
14863
15235
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
14864
15236
|
}
|
|
15237
|
+
let prefilterPlan;
|
|
15238
|
+
if (whereCapability.capability === "SUPERSET_PREFILTER") {
|
|
15239
|
+
const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, cteCache);
|
|
15240
|
+
const decomposition = decomposeRelativeDatePrefilter(stmt, resolver);
|
|
15241
|
+
if (decomposition.eligible && allowRelativeDatePrefilterPlan(stmt, decomposition)) {
|
|
15242
|
+
prefilterPlan = decomposition.plan;
|
|
15243
|
+
}
|
|
15244
|
+
}
|
|
14865
15245
|
const staticMode = resolveSelectMode(stmt);
|
|
14866
15246
|
const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
|
|
14867
15247
|
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
@@ -14892,14 +15272,18 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
14892
15272
|
cacheContext,
|
|
14893
15273
|
cteCache,
|
|
14894
15274
|
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
14895
|
-
orderMeta
|
|
15275
|
+
orderMeta,
|
|
15276
|
+
prefilterPlan
|
|
14896
15277
|
);
|
|
14897
15278
|
}
|
|
14898
15279
|
} catch (error) {
|
|
14899
15280
|
throwCompleteInputError(completePolicy, error);
|
|
14900
15281
|
}
|
|
14901
15282
|
if (captureColumnMeta) {
|
|
14902
|
-
materializedMetaBySelectResult.set(
|
|
15283
|
+
materializedMetaBySelectResult.set(
|
|
15284
|
+
result,
|
|
15285
|
+
await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache, forLibraryCapture)
|
|
15286
|
+
);
|
|
14903
15287
|
}
|
|
14904
15288
|
return result;
|
|
14905
15289
|
}
|
|
@@ -15667,7 +16051,7 @@ function selectNeedsSourceColumnMeta(stmt) {
|
|
|
15667
16051
|
(column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "CASE_COL" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX" || column.func === "MODE")
|
|
15668
16052
|
);
|
|
15669
16053
|
}
|
|
15670
|
-
async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
|
|
16054
|
+
async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables, forLibraryCapture = false) {
|
|
15671
16055
|
const physicalInfos = /* @__PURE__ */ new Map();
|
|
15672
16056
|
if (selectNeedsSourceColumnMeta(stmt)) {
|
|
15673
16057
|
await Promise.all(physicalSelectTables(stmt).map(async (table) => {
|
|
@@ -15677,6 +16061,7 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
15677
16061
|
}));
|
|
15678
16062
|
}
|
|
15679
16063
|
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
16064
|
+
const canExposePublicSource = forLibraryCapture && materializedTables === void 0 && tables.every((table) => table.cteName === null);
|
|
15680
16065
|
const resolveField2 = (ref) => {
|
|
15681
16066
|
if (ref.tableAlias !== null) {
|
|
15682
16067
|
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
@@ -15706,18 +16091,33 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
15706
16091
|
});
|
|
15707
16092
|
return matches.length === 1 ? matches[0] : void 0;
|
|
15708
16093
|
};
|
|
16094
|
+
const resolvePublicSourceApp = (ref) => {
|
|
16095
|
+
if (!canExposePublicSource) return void 0;
|
|
16096
|
+
const matches = tables.filter((table) => {
|
|
16097
|
+
if (ref.tableAlias !== null && effectiveTableAlias(table) !== ref.tableAlias) return false;
|
|
16098
|
+
const fieldCode = fieldCodeForTypeLookup(table, ref.field);
|
|
16099
|
+
return physicalInfos.get(table.appId)?.has(fieldCode) === true || systemColumnMeta(ref.field) !== void 0;
|
|
16100
|
+
});
|
|
16101
|
+
return matches.length === 1 ? matches[0].appId : void 0;
|
|
16102
|
+
};
|
|
16103
|
+
const withPublicSource = (meta, ref) => {
|
|
16104
|
+
const publicSourceApp = resolvePublicSourceApp(ref);
|
|
16105
|
+
return meta && publicSourceApp !== void 0 ? { ...meta, publicSourceApp } : meta;
|
|
16106
|
+
};
|
|
15709
16107
|
const inferred = /* @__PURE__ */ new Map();
|
|
15710
16108
|
const hasWildcard = stmt.columns.some((column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD");
|
|
15711
16109
|
if (stmt.columns.length === 1 && (stmt.columns[0].type === "WILDCARD" || stmt.columns[0].type === "PARENT_WILDCARD")) {
|
|
15712
16110
|
for (const output of outputColumns) {
|
|
15713
|
-
const
|
|
16111
|
+
const ref = aggregateFieldRef(output);
|
|
16112
|
+
const meta = withPublicSource(resolveField2(ref), ref);
|
|
15714
16113
|
if (meta) inferred.set(output, meta);
|
|
15715
16114
|
}
|
|
15716
16115
|
return inferred;
|
|
15717
16116
|
}
|
|
15718
16117
|
if (hasWildcard) {
|
|
15719
16118
|
for (const output of outputColumns) {
|
|
15720
|
-
const
|
|
16119
|
+
const ref = aggregateFieldRef(output);
|
|
16120
|
+
const meta = withPublicSource(resolveField2(ref), ref);
|
|
15721
16121
|
if (meta) inferred.set(output, meta);
|
|
15722
16122
|
}
|
|
15723
16123
|
}
|
|
@@ -15729,7 +16129,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
15729
16129
|
if (!output) return;
|
|
15730
16130
|
let meta;
|
|
15731
16131
|
if (column.type === "FIELD") {
|
|
15732
|
-
|
|
16132
|
+
const ref = aggregateFieldRef(column.field);
|
|
16133
|
+
meta = withPublicSource(resolveField2(ref), ref);
|
|
15733
16134
|
} else if (column.type === "AGGREGATE") {
|
|
15734
16135
|
if (column.func === "GROUP_CONCAT") {
|
|
15735
16136
|
meta = syntheticColumnMeta("string");
|
|
@@ -15773,8 +16174,15 @@ function mergeUnionColumnMeta(left, right) {
|
|
|
15773
16174
|
const a = leftMeta?.get(column);
|
|
15774
16175
|
const rightColumn = right.columns[index];
|
|
15775
16176
|
const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
|
|
15776
|
-
if (a && b)
|
|
15777
|
-
|
|
16177
|
+
if (a && b) {
|
|
16178
|
+
const combined = mergeExpressionColumnMeta([a, b]);
|
|
16179
|
+
const publicSourceApp = a.publicSourceApp === b.publicSourceApp ? a.publicSourceApp : void 0;
|
|
16180
|
+
const { publicSourceApp: _discarded, ...withoutPublicSource } = combined;
|
|
16181
|
+
merged.set(
|
|
16182
|
+
column,
|
|
16183
|
+
publicSourceApp === void 0 ? withoutPublicSource : { ...withoutPublicSource, publicSourceApp }
|
|
16184
|
+
);
|
|
16185
|
+
} else if (a || b) merged.set(column, unknownStringColumnMeta());
|
|
15778
16186
|
});
|
|
15779
16187
|
return merged;
|
|
15780
16188
|
}
|
|
@@ -15810,7 +16218,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
15810
16218
|
};
|
|
15811
16219
|
return { row, having };
|
|
15812
16220
|
}
|
|
15813
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
|
|
16221
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta, prefilterPlan) {
|
|
15814
16222
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
15815
16223
|
const warnings = /* @__PURE__ */ new Set();
|
|
15816
16224
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -15836,6 +16244,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
15836
16244
|
validateKlikePushdownPlan(pushdownPlan);
|
|
15837
16245
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
15838
16246
|
const tableConditions = pushdownPlan.joinConditions;
|
|
16247
|
+
if (prefilterPlan && allowOriginalWherePushdown) {
|
|
16248
|
+
throw new Error("internal error: relative-date prefilter must disable original WHERE pushdown.");
|
|
16249
|
+
}
|
|
16250
|
+
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : mainPushDown;
|
|
15839
16251
|
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
15840
16252
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
15841
16253
|
stmt,
|
|
@@ -15846,7 +16258,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
15846
16258
|
true,
|
|
15847
16259
|
options.onLimitReached ?? "error",
|
|
15848
16260
|
warnings,
|
|
15849
|
-
|
|
16261
|
+
mainFetchCondition,
|
|
15850
16262
|
allowOriginalWherePushdown
|
|
15851
16263
|
);
|
|
15852
16264
|
const parallelJoins = [];
|
|
@@ -15927,15 +16339,16 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
15927
16339
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
15928
16340
|
havingFieldSemanticsResolver,
|
|
15929
16341
|
aggregateSortKindResolver,
|
|
15930
|
-
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
16342
|
+
appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
|
|
16343
|
+
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : {},
|
|
15931
16344
|
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt)
|
|
15932
16345
|
});
|
|
15933
16346
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
15934
16347
|
}
|
|
15935
|
-
async function executeUnion(stmt, client, options, cacheContext) {
|
|
16348
|
+
async function executeUnion(stmt, client, options, cacheContext, captureColumnMeta = false, forLibraryCapture = false) {
|
|
15936
16349
|
const [leftResult, rightResult] = await Promise.all([
|
|
15937
|
-
stmt.left.type === "UNION" ? executeUnion(stmt.left, client, options, cacheContext) : executeSelect(stmt.left, client, options, cacheContext),
|
|
15938
|
-
executeSelect(stmt.right, client, options, cacheContext)
|
|
16350
|
+
stmt.left.type === "UNION" ? executeUnion(stmt.left, client, options, cacheContext, captureColumnMeta, forLibraryCapture) : executeSelect(stmt.left, client, options, cacheContext, void 0, captureColumnMeta, forLibraryCapture),
|
|
16351
|
+
executeSelect(stmt.right, client, options, cacheContext, void 0, captureColumnMeta, forLibraryCapture)
|
|
15939
16352
|
]);
|
|
15940
16353
|
const leftCols = leftResult.columns;
|
|
15941
16354
|
const rightCols = rightResult.columns;
|
|
@@ -15948,7 +16361,11 @@ async function executeUnion(stmt, client, options, cacheContext) {
|
|
|
15948
16361
|
});
|
|
15949
16362
|
const combined = [...leftResult.rows, ...remappedRight];
|
|
15950
16363
|
const rows = stmt.all ? combined : deduplicateRows(combined, leftCols);
|
|
15951
|
-
|
|
16364
|
+
const result = { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
|
|
16365
|
+
if (captureColumnMeta) {
|
|
16366
|
+
materializedMetaBySelectResult.set(result, mergeUnionColumnMeta(leftResult, rightResult));
|
|
16367
|
+
}
|
|
16368
|
+
return result;
|
|
15952
16369
|
}
|
|
15953
16370
|
function deduplicateRows(rows, columns) {
|
|
15954
16371
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -15961,7 +16378,7 @@ function deduplicateRows(rows, columns) {
|
|
|
15961
16378
|
}
|
|
15962
16379
|
async function executeWith(stmt, client, options, cacheContext, seed, captureColumnMeta = false) {
|
|
15963
16380
|
if ((seed == null || seed.size === 0) && canInlineSingleCte(stmt)) {
|
|
15964
|
-
return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext, void 0, captureColumnMeta);
|
|
16381
|
+
return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext, void 0, captureColumnMeta, false);
|
|
15965
16382
|
}
|
|
15966
16383
|
const cteCache = new Map(seed ?? []);
|
|
15967
16384
|
for (const cte of stmt.ctes) {
|
|
@@ -16006,7 +16423,7 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
|
|
|
16006
16423
|
}
|
|
16007
16424
|
const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
|
|
16008
16425
|
if (!hasCteRef) {
|
|
16009
|
-
return executeSelect(query, client, options, cacheContext, cteCache, captureColumnMeta);
|
|
16426
|
+
return executeSelect(query, client, options, cacheContext, cteCache, captureColumnMeta, false);
|
|
16010
16427
|
}
|
|
16011
16428
|
const result = await executeFullScanWithCte(query, client, options, cteCache, cacheContext);
|
|
16012
16429
|
if (captureColumnMeta) {
|
|
@@ -19732,6 +20149,102 @@ function explainMetadataLines(analysis) {
|
|
|
19732
20149
|
...[...analysis.numberPrecisionApps].sort((a, b) => a - b).map((appId) => ` metadata API: number precision APP${appId}`)
|
|
19733
20150
|
];
|
|
19734
20151
|
}
|
|
20152
|
+
function renderResidualOperator(op) {
|
|
20153
|
+
switch (op) {
|
|
20154
|
+
case "NOT_LIKE":
|
|
20155
|
+
return "NOT LIKE";
|
|
20156
|
+
case "NOT_KLIKE":
|
|
20157
|
+
return "NOT KLIKE";
|
|
20158
|
+
case "NOT_IN":
|
|
20159
|
+
return "NOT IN";
|
|
20160
|
+
case "LIKE":
|
|
20161
|
+
case "KLIKE":
|
|
20162
|
+
case "IN":
|
|
20163
|
+
case "=":
|
|
20164
|
+
case "!=":
|
|
20165
|
+
case "<>":
|
|
20166
|
+
case ">":
|
|
20167
|
+
case "<":
|
|
20168
|
+
case ">=":
|
|
20169
|
+
case "<=":
|
|
20170
|
+
return op;
|
|
20171
|
+
default:
|
|
20172
|
+
return "<op>";
|
|
20173
|
+
}
|
|
20174
|
+
}
|
|
20175
|
+
function relativeReasonOperator(op) {
|
|
20176
|
+
return op === "<>" ? "!=" : op;
|
|
20177
|
+
}
|
|
20178
|
+
function renderResidualValue(node) {
|
|
20179
|
+
if (node === null || typeof node !== "object") return "<expr>";
|
|
20180
|
+
const value = node;
|
|
20181
|
+
switch (value["type"]) {
|
|
20182
|
+
case "FIELD":
|
|
20183
|
+
return typeof value["field"] === "string" ? `${typeof value["tableAlias"] === "string" ? `${value["tableAlias"]}.` : ""}${value["field"]}` : "<expr>";
|
|
20184
|
+
case "FIELD_REF":
|
|
20185
|
+
return typeof value["field"] === "string" ? value["field"] : "<expr>";
|
|
20186
|
+
case "NUMBER":
|
|
20187
|
+
return typeof value["raw"] === "string" ? value["raw"] : typeof value["value"] === "number" ? String(value["value"]) : "<expr>";
|
|
20188
|
+
case "STRING":
|
|
20189
|
+
return typeof value["value"] === "string" ? `'${value["value"].replace(/'/g, "''")}'` : "<expr>";
|
|
20190
|
+
case "VARIABLE":
|
|
20191
|
+
return typeof value["name"] === "string" ? `@${value["name"]}` : "<expr>";
|
|
20192
|
+
case "VARIABLE_IN_LIST":
|
|
20193
|
+
return typeof value["name"] === "string" ? `@${value["name"]}` : "<expr>";
|
|
20194
|
+
case "STRING_FUNC": {
|
|
20195
|
+
if (typeof value["func"] !== "string" || !Array.isArray(value["args"])) return "<expr>";
|
|
20196
|
+
return `${value["func"]}(${value["args"].map(renderResidualValue).join(", ")})`;
|
|
20197
|
+
}
|
|
20198
|
+
case "FUNC_FIELD":
|
|
20199
|
+
case "ARITH_FIELD":
|
|
20200
|
+
case "CASE_FIELD":
|
|
20201
|
+
case "ARITH_VALUE":
|
|
20202
|
+
case "CASE_VALUE":
|
|
20203
|
+
return renderResidualValue(value["expr"]);
|
|
20204
|
+
case "GROUPING_FIELD":
|
|
20205
|
+
return `GROUPING(${renderResidualValue(value["ref"])})`;
|
|
20206
|
+
case "GROUPING_REF":
|
|
20207
|
+
return renderResidualValue(value["field"]);
|
|
20208
|
+
case "ARITH":
|
|
20209
|
+
case "SCALAR_ARITH":
|
|
20210
|
+
case "CONCAT_OP":
|
|
20211
|
+
return `(${renderResidualValue(value["left"])} ${typeof value["op"] === "string" ? value["op"] : value["type"] === "CONCAT_OP" ? "||" : "<op>"} ${renderResidualValue(value["right"])})`;
|
|
20212
|
+
case "KINTONE_FUNC":
|
|
20213
|
+
return typeof value["name"] === "string" ? `${value["name"]}(...)` : "<expr>";
|
|
20214
|
+
case "IN_LIST":
|
|
20215
|
+
return Array.isArray(value["values"]) ? `(${value["values"].map(renderResidualValue).join(", ")})` : "<expr>";
|
|
20216
|
+
case "ARRAY":
|
|
20217
|
+
return Array.isArray(value["elements"]) ? `[${value["elements"].map(renderResidualValue).join(", ")}]` : "<expr>";
|
|
20218
|
+
case "CASE":
|
|
20219
|
+
case "CASE_WHEN":
|
|
20220
|
+
return "CASE ... END";
|
|
20221
|
+
default:
|
|
20222
|
+
return "<expr>";
|
|
20223
|
+
}
|
|
20224
|
+
}
|
|
20225
|
+
function renderRelativeDateResidualWhere(where) {
|
|
20226
|
+
try {
|
|
20227
|
+
const node = where;
|
|
20228
|
+
switch (node["type"]) {
|
|
20229
|
+
case "BINARY":
|
|
20230
|
+
return `${renderResidualValue(node["left"])} ${renderResidualOperator(node["op"])} ${renderResidualValue(node["right"])}`;
|
|
20231
|
+
case "NULL_CHECK":
|
|
20232
|
+
return `${renderResidualValue(node["field"])} IS ${node["not"] === true ? "NOT " : ""}NULL`;
|
|
20233
|
+
case "LOGICAL":
|
|
20234
|
+
return `(${renderRelativeDateResidualWhere(node["left"])} ${node["op"] === "AND" || node["op"] === "OR" ? node["op"] : "<op>"} ${renderRelativeDateResidualWhere(node["right"])})`;
|
|
20235
|
+
case "NOT":
|
|
20236
|
+
return `NOT (${renderRelativeDateResidualWhere(node["expr"])})`;
|
|
20237
|
+
case "GROUP":
|
|
20238
|
+
return `(${renderRelativeDateResidualWhere(node["expr"])})`;
|
|
20239
|
+
case "BOOLEAN":
|
|
20240
|
+
return node["value"] === true ? "TRUE" : node["value"] === false ? "FALSE" : "<expr>";
|
|
20241
|
+
default:
|
|
20242
|
+
return "<expr>";
|
|
20243
|
+
}
|
|
20244
|
+
} catch {
|
|
20245
|
+
return "<expr>";
|
|
20246
|
+
}
|
|
20247
|
+
}
|
|
19735
20248
|
function relativeDateExplainLines(plan) {
|
|
19736
20249
|
if (!plan.hasRelativeDate) return [];
|
|
19737
20250
|
if (!plan.allowed && plan.rejection) {
|
|
@@ -19745,6 +20258,32 @@ function relativeDateExplainLines(plan) {
|
|
|
19745
20258
|
}
|
|
19746
20259
|
const lines = [];
|
|
19747
20260
|
for (const node of plan.nodes) {
|
|
20261
|
+
const prefilterPlan = node.prefilterPlan;
|
|
20262
|
+
if (node.allowed && prefilterPlan?.prefilterWhere && prefilterPlan.residualWhere) {
|
|
20263
|
+
for (const leaf of prefilterPlan.exactRelativeLeaves) {
|
|
20264
|
+
const functionName = leaf.right.type === "KINTONE_FUNC" ? leaf.right.name : "(unknown)";
|
|
20265
|
+
const field = leaf.left.type === "FIELD" ? leaf.left.field : void 0;
|
|
20266
|
+
const operator = relativeReasonOperator(leaf.op);
|
|
20267
|
+
const detail = node.capability?.reasons.find(
|
|
20268
|
+
(reason) => reason.functionName === functionName && (field === void 0 || reason.field === field) && reason.operator === operator
|
|
20269
|
+
);
|
|
20270
|
+
lines.push(
|
|
20271
|
+
` relative date function: ${functionName}`,
|
|
20272
|
+
" relative date evaluation: kintone server exact prefilter",
|
|
20273
|
+
` field: ${detail?.field ?? field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
20274
|
+
` operator: ${detail?.operator ?? operator}`
|
|
20275
|
+
);
|
|
20276
|
+
}
|
|
20277
|
+
const serverPrefilter = whereToKintone(prefilterPlan.prefilterWhere);
|
|
20278
|
+
lines.push(
|
|
20279
|
+
" where capability: SUPERSET_PREFILTER",
|
|
20280
|
+
` server prefilter: ${serverPrefilter}`,
|
|
20281
|
+
` client residual: ${renderRelativeDateResidualWhere(prefilterPlan.residualWhere)}`,
|
|
20282
|
+
" relative date client evaluations: 0",
|
|
20283
|
+
` kintone query: ${serverPrefilter}`
|
|
20284
|
+
);
|
|
20285
|
+
continue;
|
|
20286
|
+
}
|
|
19748
20287
|
for (const functionName of node.functionNames) {
|
|
19749
20288
|
const detail = node.capability?.reasons.find(
|
|
19750
20289
|
(reason) => reason.functionName === functionName
|