@rex0220/kintone-sql-tools 3.20.0 → 3.21.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 +497 -12
- package/dist-engine/index.cjs +8 -8
- package/dist-engine/index.mjs +8 -8
- package/dist-engine/ksql-engine.umd.js +8 -8
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +47 -8
- package/dist-engine/meta/esm.json +47 -8
- package/dist-engine/meta/umd.json +47 -8
- package/dist-mcp/ksql-mcp.js +500 -14
- 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 {
|
|
@@ -13753,7 +14093,16 @@ async function assertRelativeDateExecutionPlan(stmt, client, cacheContext) {
|
|
|
13753
14093
|
async function resolveRelativeDateExecutionPlan(stmt, client, cacheContext) {
|
|
13754
14094
|
return buildRelativeDatePushdownPlan(stmt, {
|
|
13755
14095
|
select: (select) => resolveSelectWhereCapability(select, client, cacheContext),
|
|
13756
|
-
dml: (dml) => resolveDmlWhereCapability(dml, client, cacheContext)
|
|
14096
|
+
dml: (dml) => resolveDmlWhereCapability(dml, client, cacheContext),
|
|
14097
|
+
prefilterDecomposition: async (select) => {
|
|
14098
|
+
if (select.where === null) return null;
|
|
14099
|
+
const resolver = await buildWhereFieldSemanticsResolver(
|
|
14100
|
+
select,
|
|
14101
|
+
client,
|
|
14102
|
+
cacheContext
|
|
14103
|
+
);
|
|
14104
|
+
return decomposeRelativeDatePrefilter(select, resolver);
|
|
14105
|
+
}
|
|
13757
14106
|
});
|
|
13758
14107
|
}
|
|
13759
14108
|
async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
@@ -14862,6 +15211,14 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
14862
15211
|
if (whereCapability.capability === "UNSUPPORTED") {
|
|
14863
15212
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
14864
15213
|
}
|
|
15214
|
+
let prefilterPlan;
|
|
15215
|
+
if (whereCapability.capability === "SUPERSET_PREFILTER") {
|
|
15216
|
+
const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, cteCache);
|
|
15217
|
+
const decomposition = decomposeRelativeDatePrefilter(stmt, resolver);
|
|
15218
|
+
if (decomposition.eligible && allowRelativeDatePrefilterPlan(stmt, decomposition)) {
|
|
15219
|
+
prefilterPlan = decomposition.plan;
|
|
15220
|
+
}
|
|
15221
|
+
}
|
|
14865
15222
|
const staticMode = resolveSelectMode(stmt);
|
|
14866
15223
|
const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
|
|
14867
15224
|
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
@@ -14892,7 +15249,8 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
14892
15249
|
cacheContext,
|
|
14893
15250
|
cteCache,
|
|
14894
15251
|
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
14895
|
-
orderMeta
|
|
15252
|
+
orderMeta,
|
|
15253
|
+
prefilterPlan
|
|
14896
15254
|
);
|
|
14897
15255
|
}
|
|
14898
15256
|
} catch (error) {
|
|
@@ -15810,7 +16168,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
15810
16168
|
};
|
|
15811
16169
|
return { row, having };
|
|
15812
16170
|
}
|
|
15813
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
|
|
16171
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta, prefilterPlan) {
|
|
15814
16172
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
15815
16173
|
const warnings = /* @__PURE__ */ new Set();
|
|
15816
16174
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -15836,6 +16194,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
15836
16194
|
validateKlikePushdownPlan(pushdownPlan);
|
|
15837
16195
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
15838
16196
|
const tableConditions = pushdownPlan.joinConditions;
|
|
16197
|
+
if (prefilterPlan && allowOriginalWherePushdown) {
|
|
16198
|
+
throw new Error("internal error: relative-date prefilter must disable original WHERE pushdown.");
|
|
16199
|
+
}
|
|
16200
|
+
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : mainPushDown;
|
|
15839
16201
|
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
15840
16202
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
15841
16203
|
stmt,
|
|
@@ -15846,7 +16208,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
15846
16208
|
true,
|
|
15847
16209
|
options.onLimitReached ?? "error",
|
|
15848
16210
|
warnings,
|
|
15849
|
-
|
|
16211
|
+
mainFetchCondition,
|
|
15850
16212
|
allowOriginalWherePushdown
|
|
15851
16213
|
);
|
|
15852
16214
|
const parallelJoins = [];
|
|
@@ -15927,7 +16289,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
15927
16289
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
15928
16290
|
havingFieldSemanticsResolver,
|
|
15929
16291
|
aggregateSortKindResolver,
|
|
15930
|
-
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
16292
|
+
appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
|
|
16293
|
+
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : {},
|
|
15931
16294
|
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt)
|
|
15932
16295
|
});
|
|
15933
16296
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
@@ -19732,6 +20095,102 @@ function explainMetadataLines(analysis) {
|
|
|
19732
20095
|
...[...analysis.numberPrecisionApps].sort((a, b) => a - b).map((appId) => ` metadata API: number precision APP${appId}`)
|
|
19733
20096
|
];
|
|
19734
20097
|
}
|
|
20098
|
+
function renderResidualOperator(op) {
|
|
20099
|
+
switch (op) {
|
|
20100
|
+
case "NOT_LIKE":
|
|
20101
|
+
return "NOT LIKE";
|
|
20102
|
+
case "NOT_KLIKE":
|
|
20103
|
+
return "NOT KLIKE";
|
|
20104
|
+
case "NOT_IN":
|
|
20105
|
+
return "NOT IN";
|
|
20106
|
+
case "LIKE":
|
|
20107
|
+
case "KLIKE":
|
|
20108
|
+
case "IN":
|
|
20109
|
+
case "=":
|
|
20110
|
+
case "!=":
|
|
20111
|
+
case "<>":
|
|
20112
|
+
case ">":
|
|
20113
|
+
case "<":
|
|
20114
|
+
case ">=":
|
|
20115
|
+
case "<=":
|
|
20116
|
+
return op;
|
|
20117
|
+
default:
|
|
20118
|
+
return "<op>";
|
|
20119
|
+
}
|
|
20120
|
+
}
|
|
20121
|
+
function relativeReasonOperator(op) {
|
|
20122
|
+
return op === "<>" ? "!=" : op;
|
|
20123
|
+
}
|
|
20124
|
+
function renderResidualValue(node) {
|
|
20125
|
+
if (node === null || typeof node !== "object") return "<expr>";
|
|
20126
|
+
const value = node;
|
|
20127
|
+
switch (value["type"]) {
|
|
20128
|
+
case "FIELD":
|
|
20129
|
+
return typeof value["field"] === "string" ? `${typeof value["tableAlias"] === "string" ? `${value["tableAlias"]}.` : ""}${value["field"]}` : "<expr>";
|
|
20130
|
+
case "FIELD_REF":
|
|
20131
|
+
return typeof value["field"] === "string" ? value["field"] : "<expr>";
|
|
20132
|
+
case "NUMBER":
|
|
20133
|
+
return typeof value["raw"] === "string" ? value["raw"] : typeof value["value"] === "number" ? String(value["value"]) : "<expr>";
|
|
20134
|
+
case "STRING":
|
|
20135
|
+
return typeof value["value"] === "string" ? `'${value["value"].replace(/'/g, "''")}'` : "<expr>";
|
|
20136
|
+
case "VARIABLE":
|
|
20137
|
+
return typeof value["name"] === "string" ? `@${value["name"]}` : "<expr>";
|
|
20138
|
+
case "VARIABLE_IN_LIST":
|
|
20139
|
+
return typeof value["name"] === "string" ? `@${value["name"]}` : "<expr>";
|
|
20140
|
+
case "STRING_FUNC": {
|
|
20141
|
+
if (typeof value["func"] !== "string" || !Array.isArray(value["args"])) return "<expr>";
|
|
20142
|
+
return `${value["func"]}(${value["args"].map(renderResidualValue).join(", ")})`;
|
|
20143
|
+
}
|
|
20144
|
+
case "FUNC_FIELD":
|
|
20145
|
+
case "ARITH_FIELD":
|
|
20146
|
+
case "CASE_FIELD":
|
|
20147
|
+
case "ARITH_VALUE":
|
|
20148
|
+
case "CASE_VALUE":
|
|
20149
|
+
return renderResidualValue(value["expr"]);
|
|
20150
|
+
case "GROUPING_FIELD":
|
|
20151
|
+
return `GROUPING(${renderResidualValue(value["ref"])})`;
|
|
20152
|
+
case "GROUPING_REF":
|
|
20153
|
+
return renderResidualValue(value["field"]);
|
|
20154
|
+
case "ARITH":
|
|
20155
|
+
case "SCALAR_ARITH":
|
|
20156
|
+
case "CONCAT_OP":
|
|
20157
|
+
return `(${renderResidualValue(value["left"])} ${typeof value["op"] === "string" ? value["op"] : value["type"] === "CONCAT_OP" ? "||" : "<op>"} ${renderResidualValue(value["right"])})`;
|
|
20158
|
+
case "KINTONE_FUNC":
|
|
20159
|
+
return typeof value["name"] === "string" ? `${value["name"]}(...)` : "<expr>";
|
|
20160
|
+
case "IN_LIST":
|
|
20161
|
+
return Array.isArray(value["values"]) ? `(${value["values"].map(renderResidualValue).join(", ")})` : "<expr>";
|
|
20162
|
+
case "ARRAY":
|
|
20163
|
+
return Array.isArray(value["elements"]) ? `[${value["elements"].map(renderResidualValue).join(", ")}]` : "<expr>";
|
|
20164
|
+
case "CASE":
|
|
20165
|
+
case "CASE_WHEN":
|
|
20166
|
+
return "CASE ... END";
|
|
20167
|
+
default:
|
|
20168
|
+
return "<expr>";
|
|
20169
|
+
}
|
|
20170
|
+
}
|
|
20171
|
+
function renderRelativeDateResidualWhere(where) {
|
|
20172
|
+
try {
|
|
20173
|
+
const node = where;
|
|
20174
|
+
switch (node["type"]) {
|
|
20175
|
+
case "BINARY":
|
|
20176
|
+
return `${renderResidualValue(node["left"])} ${renderResidualOperator(node["op"])} ${renderResidualValue(node["right"])}`;
|
|
20177
|
+
case "NULL_CHECK":
|
|
20178
|
+
return `${renderResidualValue(node["field"])} IS ${node["not"] === true ? "NOT " : ""}NULL`;
|
|
20179
|
+
case "LOGICAL":
|
|
20180
|
+
return `(${renderRelativeDateResidualWhere(node["left"])} ${node["op"] === "AND" || node["op"] === "OR" ? node["op"] : "<op>"} ${renderRelativeDateResidualWhere(node["right"])})`;
|
|
20181
|
+
case "NOT":
|
|
20182
|
+
return `NOT (${renderRelativeDateResidualWhere(node["expr"])})`;
|
|
20183
|
+
case "GROUP":
|
|
20184
|
+
return `(${renderRelativeDateResidualWhere(node["expr"])})`;
|
|
20185
|
+
case "BOOLEAN":
|
|
20186
|
+
return node["value"] === true ? "TRUE" : node["value"] === false ? "FALSE" : "<expr>";
|
|
20187
|
+
default:
|
|
20188
|
+
return "<expr>";
|
|
20189
|
+
}
|
|
20190
|
+
} catch {
|
|
20191
|
+
return "<expr>";
|
|
20192
|
+
}
|
|
20193
|
+
}
|
|
19735
20194
|
function relativeDateExplainLines(plan) {
|
|
19736
20195
|
if (!plan.hasRelativeDate) return [];
|
|
19737
20196
|
if (!plan.allowed && plan.rejection) {
|
|
@@ -19745,6 +20204,32 @@ function relativeDateExplainLines(plan) {
|
|
|
19745
20204
|
}
|
|
19746
20205
|
const lines = [];
|
|
19747
20206
|
for (const node of plan.nodes) {
|
|
20207
|
+
const prefilterPlan = node.prefilterPlan;
|
|
20208
|
+
if (node.allowed && prefilterPlan?.prefilterWhere && prefilterPlan.residualWhere) {
|
|
20209
|
+
for (const leaf of prefilterPlan.exactRelativeLeaves) {
|
|
20210
|
+
const functionName = leaf.right.type === "KINTONE_FUNC" ? leaf.right.name : "(unknown)";
|
|
20211
|
+
const field = leaf.left.type === "FIELD" ? leaf.left.field : void 0;
|
|
20212
|
+
const operator = relativeReasonOperator(leaf.op);
|
|
20213
|
+
const detail = node.capability?.reasons.find(
|
|
20214
|
+
(reason) => reason.functionName === functionName && (field === void 0 || reason.field === field) && reason.operator === operator
|
|
20215
|
+
);
|
|
20216
|
+
lines.push(
|
|
20217
|
+
` relative date function: ${functionName}`,
|
|
20218
|
+
" relative date evaluation: kintone server exact prefilter",
|
|
20219
|
+
` field: ${detail?.field ?? field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
20220
|
+
` operator: ${detail?.operator ?? operator}`
|
|
20221
|
+
);
|
|
20222
|
+
}
|
|
20223
|
+
const serverPrefilter = whereToKintone(prefilterPlan.prefilterWhere);
|
|
20224
|
+
lines.push(
|
|
20225
|
+
" where capability: SUPERSET_PREFILTER",
|
|
20226
|
+
` server prefilter: ${serverPrefilter}`,
|
|
20227
|
+
` client residual: ${renderRelativeDateResidualWhere(prefilterPlan.residualWhere)}`,
|
|
20228
|
+
" relative date client evaluations: 0",
|
|
20229
|
+
` kintone query: ${serverPrefilter}`
|
|
20230
|
+
);
|
|
20231
|
+
continue;
|
|
20232
|
+
}
|
|
19748
20233
|
for (const functionName of node.functionNames) {
|
|
19749
20234
|
const detail = node.capability?.reasons.find(
|
|
19750
20235
|
(reason) => reason.functionName === functionName
|