@harperfast/harper 5.2.0-alpha.6 → 5.2.0-beta.1
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/dataLayer/harperBridge/ResourceBridge.ts +7 -0
- package/dist/dataLayer/harperBridge/ResourceBridge.js +8 -0
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/resources/Resource.js +5 -2
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/Table.d.ts +3 -1
- package/dist/resources/Table.js +13 -3
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/databases.js +21 -0
- package/dist/resources/databases.js.map +1 -1
- package/dist/sqlEngine/binder/bind.d.ts +72 -0
- package/dist/sqlEngine/binder/bind.js +289 -0
- package/dist/sqlEngine/binder/bind.js.map +1 -0
- package/dist/sqlEngine/config.d.ts +35 -0
- package/dist/sqlEngine/config.js +62 -0
- package/dist/sqlEngine/config.js.map +1 -0
- package/dist/sqlEngine/diff/differential.d.ts +23 -0
- package/dist/sqlEngine/diff/differential.js +90 -0
- package/dist/sqlEngine/diff/differential.js.map +1 -0
- package/dist/sqlEngine/errors.d.ts +22 -0
- package/dist/sqlEngine/errors.js +36 -0
- package/dist/sqlEngine/errors.js.map +1 -0
- package/dist/sqlEngine/executor/runMutation.d.ts +35 -0
- package/dist/sqlEngine/executor/runMutation.js +352 -0
- package/dist/sqlEngine/executor/runMutation.js.map +1 -0
- package/dist/sqlEngine/executor/runSelect.d.ts +7 -0
- package/dist/sqlEngine/executor/runSelect.js +14 -0
- package/dist/sqlEngine/executor/runSelect.js.map +1 -0
- package/dist/sqlEngine/expressions/compile.d.ts +24 -0
- package/dist/sqlEngine/expressions/compile.js +326 -0
- package/dist/sqlEngine/expressions/compile.js.map +1 -0
- package/dist/sqlEngine/functions/aggregates.d.ts +12 -0
- package/dist/sqlEngine/functions/aggregates.js +299 -0
- package/dist/sqlEngine/functions/aggregates.js.map +1 -0
- package/dist/sqlEngine/functions/registry.d.ts +36 -0
- package/dist/sqlEngine/functions/registry.js +33 -0
- package/dist/sqlEngine/functions/registry.js.map +1 -0
- package/dist/sqlEngine/functions/standard.d.ts +9 -0
- package/dist/sqlEngine/functions/standard.js +86 -0
- package/dist/sqlEngine/functions/standard.js.map +1 -0
- package/dist/sqlEngine/index.d.ts +29 -0
- package/dist/sqlEngine/index.js +62 -0
- package/dist/sqlEngine/index.js.map +1 -0
- package/dist/sqlEngine/logical/build.d.ts +20 -0
- package/dist/sqlEngine/logical/build.js +326 -0
- package/dist/sqlEngine/logical/build.js.map +1 -0
- package/dist/sqlEngine/logical/op.d.ts +105 -0
- package/dist/sqlEngine/logical/op.js +10 -0
- package/dist/sqlEngine/logical/op.js.map +1 -0
- package/dist/sqlEngine/optimizer/joinAnalysis.d.ts +37 -0
- package/dist/sqlEngine/optimizer/joinAnalysis.js +144 -0
- package/dist/sqlEngine/optimizer/joinAnalysis.js.map +1 -0
- package/dist/sqlEngine/optimizer/optimize.d.ts +13 -0
- package/dist/sqlEngine/optimizer/optimize.js +53 -0
- package/dist/sqlEngine/optimizer/optimize.js.map +1 -0
- package/dist/sqlEngine/optimizer/ruleEngine.d.ts +7 -0
- package/dist/sqlEngine/optimizer/ruleEngine.js +26 -0
- package/dist/sqlEngine/optimizer/ruleEngine.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/limitPushdown.d.ts +19 -0
- package/dist/sqlEngine/optimizer/rules/limitPushdown.js +55 -0
- package/dist/sqlEngine/optimizer/rules/limitPushdown.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/planJoins.d.ts +19 -0
- package/dist/sqlEngine/optimizer/rules/planJoins.js +62 -0
- package/dist/sqlEngine/optimizer/rules/planJoins.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/predicateNormalize.d.ts +18 -0
- package/dist/sqlEngine/optimizer/rules/predicateNormalize.js +80 -0
- package/dist/sqlEngine/optimizer/rules/predicateNormalize.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/predicatePushdown.d.ts +11 -0
- package/dist/sqlEngine/optimizer/rules/predicatePushdown.js +48 -0
- package/dist/sqlEngine/optimizer/rules/predicatePushdown.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/projectionPushdown.d.ts +23 -0
- package/dist/sqlEngine/optimizer/rules/projectionPushdown.js +160 -0
- package/dist/sqlEngine/optimizer/rules/projectionPushdown.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/sortFromIndex.d.ts +14 -0
- package/dist/sqlEngine/optimizer/rules/sortFromIndex.js +49 -0
- package/dist/sqlEngine/optimizer/rules/sortFromIndex.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/validateScannable.d.ts +15 -0
- package/dist/sqlEngine/optimizer/rules/validateScannable.js +60 -0
- package/dist/sqlEngine/optimizer/rules/validateScannable.js.map +1 -0
- package/dist/sqlEngine/optimizer/whereToConditions.d.ts +85 -0
- package/dist/sqlEngine/optimizer/whereToConditions.js +484 -0
- package/dist/sqlEngine/optimizer/whereToConditions.js.map +1 -0
- package/dist/sqlEngine/parser/ast.d.ts +139 -0
- package/dist/sqlEngine/parser/ast.js +10 -0
- package/dist/sqlEngine/parser/ast.js.map +1 -0
- package/dist/sqlEngine/parser/normalizer.d.ts +28 -0
- package/dist/sqlEngine/parser/normalizer.js +413 -0
- package/dist/sqlEngine/parser/normalizer.js.map +1 -0
- package/dist/sqlEngine/parser/parse.d.ts +14 -0
- package/dist/sqlEngine/parser/parse.js +20 -0
- package/dist/sqlEngine/parser/parse.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalDistinct.d.ts +9 -0
- package/dist/sqlEngine/physical/PhysicalDistinct.js +29 -0
- package/dist/sqlEngine/physical/PhysicalDistinct.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalFilter.d.ts +10 -0
- package/dist/sqlEngine/physical/PhysicalFilter.js +25 -0
- package/dist/sqlEngine/physical/PhysicalFilter.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalHashAggregate.d.ts +27 -0
- package/dist/sqlEngine/physical/PhysicalHashAggregate.js +100 -0
- package/dist/sqlEngine/physical/PhysicalHashAggregate.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalHashJoin.d.ts +29 -0
- package/dist/sqlEngine/physical/PhysicalHashJoin.js +93 -0
- package/dist/sqlEngine/physical/PhysicalHashJoin.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.d.ts +36 -0
- package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.js +78 -0
- package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalIndexScan.d.ts +22 -0
- package/dist/sqlEngine/physical/PhysicalIndexScan.js +77 -0
- package/dist/sqlEngine/physical/PhysicalIndexScan.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalLimit.d.ts +5 -0
- package/dist/sqlEngine/physical/PhysicalLimit.js +28 -0
- package/dist/sqlEngine/physical/PhysicalLimit.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.d.ts +20 -0
- package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.js +51 -0
- package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalProject.d.ts +18 -0
- package/dist/sqlEngine/physical/PhysicalProject.js +117 -0
- package/dist/sqlEngine/physical/PhysicalProject.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalQualify.d.ts +13 -0
- package/dist/sqlEngine/physical/PhysicalQualify.js +29 -0
- package/dist/sqlEngine/physical/PhysicalQualify.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalSort.d.ts +12 -0
- package/dist/sqlEngine/physical/PhysicalSort.js +59 -0
- package/dist/sqlEngine/physical/PhysicalSort.js.map +1 -0
- package/dist/sqlEngine/physical/op.d.ts +11 -0
- package/dist/sqlEngine/physical/op.js +9 -0
- package/dist/sqlEngine/physical/op.js.map +1 -0
- package/dist/sqlEngine/physical/plan.d.ts +23 -0
- package/dist/sqlEngine/physical/plan.js +162 -0
- package/dist/sqlEngine/physical/plan.js.map +1 -0
- package/dist/sqlEngine/router.d.ts +26 -0
- package/dist/sqlEngine/router.js +48 -0
- package/dist/sqlEngine/router.js.map +1 -0
- package/dist/sqlEngine/types.d.ts +37 -0
- package/dist/sqlEngine/types.js +13 -0
- package/dist/sqlEngine/types.js.map +1 -0
- package/dist/sqlTranslator/index.js +7 -1
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/validation/deleteValidator.js +10 -2
- package/dist/validation/deleteValidator.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/resources/DESIGN.md +17 -16
- package/resources/Resource.ts +5 -2
- package/resources/Table.ts +23 -3
- package/resources/databases.ts +24 -0
- package/sqlTranslator/index.ts +16 -6
- package/studio/web/assets/{Chat-CTjtL8Z4.js → Chat-BZks8dVF.js} +2 -2
- package/studio/web/assets/{Chat-CTjtL8Z4.js.map → Chat-BZks8dVF.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-CafHR4Ur.js → FloatingChat-Dic8paVO.js} +4 -4
- package/studio/web/assets/{FloatingChat-CafHR4Ur.js.map → FloatingChat-Dic8paVO.js.map} +1 -1
- package/studio/web/assets/{applications-Buh_q0Vj.js → applications-uOXkeUIN.js} +2 -2
- package/studio/web/assets/{applications-Buh_q0Vj.js.map → applications-uOXkeUIN.js.map} +1 -1
- package/studio/web/assets/{index-0hXeECkS.js → index-i-2wrKhv.js} +6 -6
- package/studio/web/assets/{index-0hXeECkS.js.map → index-i-2wrKhv.js.map} +1 -1
- package/studio/web/assets/{index.lazy-B00B7VBT.js → index.lazy-Csk8eCoB.js} +4 -4
- package/studio/web/assets/{index.lazy-B00B7VBT.js.map → index.lazy-Csk8eCoB.js.map} +1 -1
- package/studio/web/assets/{profile-Cg2wwYPn.js → profile-Sb3mGDl6.js} +2 -2
- package/studio/web/assets/{profile-Cg2wwYPn.js.map → profile-Sb3mGDl6.js.map} +1 -1
- package/studio/web/assets/{setComponentFile-DCaDIvyB.js → setComponentFile-BgZcaPJ2.js} +2 -2
- package/studio/web/assets/{setComponentFile-DCaDIvyB.js.map → setComponentFile-BgZcaPJ2.js.map} +1 -1
- package/studio/web/assets/{setup-CAVcAQjK.js → setup-DKtlLgmT.js} +2 -2
- package/studio/web/assets/{setup-CAVcAQjK.js.map → setup-DKtlLgmT.js.map} +1 -1
- package/studio/web/assets/{status-BRXorNdD.js → status-B45iLeug.js} +2 -2
- package/studio/web/assets/{status-BRXorNdD.js.map → status-B45iLeug.js.map} +1 -1
- package/studio/web/assets/{swagger-ui-react-Dy1D62vO.js → swagger-ui-react-Csu4026e.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-Dy1D62vO.js.map → swagger-ui-react-Csu4026e.js.map} +1 -1
- package/studio/web/assets/{tsMode-A8gbL74v.js → tsMode-DVgxUr_l.js} +2 -2
- package/studio/web/assets/{tsMode-A8gbL74v.js.map → tsMode-DVgxUr_l.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-cDodrVcQ.js → useEntityRestURL-yfDQMV1f.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-cDodrVcQ.js.map → useEntityRestURL-yfDQMV1f.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/validation/deleteValidator.ts +11 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rule R3: predicate normalization.
|
|
4
|
+
*
|
|
5
|
+
* Maps AlaSQL operator forms to a canonical shape ahead of pushdown:
|
|
6
|
+
* - LIKE 'foo%' -> starts_with(foo)
|
|
7
|
+
* - LIKE '%foo' -> ends_with(foo)
|
|
8
|
+
* - LIKE '%foo%' -> contains(foo)
|
|
9
|
+
* - BETWEEN a AND b -> binop chain (col >= a AND col <= b) with semantic
|
|
10
|
+
* hint kept via marker for the pushdown rule
|
|
11
|
+
* - NOT (a > b) -> a <= b (NULL semantics preserved by leaving NOT in
|
|
12
|
+
* place when the operand is anything other than a
|
|
13
|
+
* comparable binop on a single column ref)
|
|
14
|
+
* - IS NULL / IS NOT NULL: left as-is (rule output for downstream).
|
|
15
|
+
*
|
|
16
|
+
* No change-of-meaning rewrites at this stage; the rule is conservative.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.normalizePredicate = normalizePredicate;
|
|
20
|
+
const NOT_OP_INVERSE = {
|
|
21
|
+
'=': '!=',
|
|
22
|
+
'!=': '=',
|
|
23
|
+
'<>': '=',
|
|
24
|
+
'<': '>=',
|
|
25
|
+
'<=': '>',
|
|
26
|
+
'>': '<=',
|
|
27
|
+
'>=': '<',
|
|
28
|
+
};
|
|
29
|
+
function normalizePredicate(expr) {
|
|
30
|
+
switch (expr.kind) {
|
|
31
|
+
case 'logical': {
|
|
32
|
+
if (expr.op === 'not' && expr.args.length === 1) {
|
|
33
|
+
const inner = normalizePredicate(expr.args[0]);
|
|
34
|
+
if (inner.kind === 'binop' && NOT_OP_INVERSE[inner.op]) {
|
|
35
|
+
return {
|
|
36
|
+
kind: 'binop',
|
|
37
|
+
op: NOT_OP_INVERSE[inner.op],
|
|
38
|
+
left: inner.left,
|
|
39
|
+
right: inner.right,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (inner.kind === 'isNull') {
|
|
43
|
+
return { ...inner, negated: !inner.negated };
|
|
44
|
+
}
|
|
45
|
+
if (inner.kind === 'between') {
|
|
46
|
+
return { ...inner, negated: !inner.negated };
|
|
47
|
+
}
|
|
48
|
+
if (inner.kind === 'in') {
|
|
49
|
+
return { ...inner, negated: !inner.negated };
|
|
50
|
+
}
|
|
51
|
+
if (inner.kind === 'like') {
|
|
52
|
+
return { ...inner, negated: !inner.negated };
|
|
53
|
+
}
|
|
54
|
+
return { kind: 'logical', op: 'not', args: [inner] };
|
|
55
|
+
}
|
|
56
|
+
return { kind: 'logical', op: expr.op, args: expr.args.map(normalizePredicate) };
|
|
57
|
+
}
|
|
58
|
+
case 'binop':
|
|
59
|
+
return { ...expr, left: normalizeChild(expr.left), right: normalizeChild(expr.right) };
|
|
60
|
+
case 'between':
|
|
61
|
+
return {
|
|
62
|
+
...expr,
|
|
63
|
+
expr: normalizeChild(expr.expr),
|
|
64
|
+
low: normalizeChild(expr.low),
|
|
65
|
+
high: normalizeChild(expr.high),
|
|
66
|
+
};
|
|
67
|
+
case 'in':
|
|
68
|
+
return { ...expr, expr: normalizeChild(expr.expr) };
|
|
69
|
+
case 'like':
|
|
70
|
+
return { ...expr, expr: normalizeChild(expr.expr), pattern: normalizeChild(expr.pattern) };
|
|
71
|
+
case 'isNull':
|
|
72
|
+
return { ...expr, expr: normalizeChild(expr.expr) };
|
|
73
|
+
default:
|
|
74
|
+
return expr;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function normalizeChild(expr) {
|
|
78
|
+
return expr;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=predicateNormalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicateNormalize.js","sourceRoot":"","sources":["../../../../sqlEngine/optimizer/rules/predicateNormalize.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;AAcH,gDA+CC;AAzDD,MAAM,cAAc,GAA6B;IAChD,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,GAAG;IACT,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,GAAG;CACT,CAAC;AAEF,SAAgB,kBAAkB,CAAC,IAAc;IAChD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS,CAAC,CAAC,CAAC;YAChB,IAAI,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;oBACxD,OAAO;wBACN,IAAI,EAAE,OAAO;wBACb,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,KAAK,EAAE,KAAK,CAAC,KAAK;qBAClB,CAAC;gBACH,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC7B,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBAC9C,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC9B,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBAC9C,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;oBACzB,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBAC9C,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC3B,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBAC9C,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAClF,CAAC;QACD,KAAK,OAAO;YACX,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxF,KAAK,SAAS;YACb,OAAO;gBACN,GAAG,IAAI;gBACP,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC/B,GAAG,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;gBAC7B,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;aAC/B,CAAC;QACH,KAAK,IAAI;YACR,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD,KAAK,MAAM;YACV,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5F,KAAK,QAAQ;YACZ,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD;YACC,OAAO,IAAI,CAAC;IACd,CAAC;AACF,CAAC;AAED,SAAS,cAAc,CAAC,IAAc;IACrC,OAAO,IAAI,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule R1: predicate pushdown.
|
|
3
|
+
*
|
|
4
|
+
* Pushes Filter into the Scan immediately below it. The portion that maps to
|
|
5
|
+
* Resource API conditions is stored on Scan.pushedFilter (consumed by the
|
|
6
|
+
* physical planner's whereToConditions). Anything that can't be expressed as
|
|
7
|
+
* a condition is left as Scan.residualFilter and applied by a wrapping
|
|
8
|
+
* PhysicalFilter at execution time.
|
|
9
|
+
*/
|
|
10
|
+
import type { LogicalPlan } from '../../logical/op.ts';
|
|
11
|
+
export declare function predicatePushdown(plan: LogicalPlan): LogicalPlan | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rule R1: predicate pushdown.
|
|
4
|
+
*
|
|
5
|
+
* Pushes Filter into the Scan immediately below it. The portion that maps to
|
|
6
|
+
* Resource API conditions is stored on Scan.pushedFilter (consumed by the
|
|
7
|
+
* physical planner's whereToConditions). Anything that can't be expressed as
|
|
8
|
+
* a condition is left as Scan.residualFilter and applied by a wrapping
|
|
9
|
+
* PhysicalFilter at execution time.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.predicatePushdown = predicatePushdown;
|
|
13
|
+
function predicatePushdown(plan) {
|
|
14
|
+
return rewrite(plan);
|
|
15
|
+
}
|
|
16
|
+
function rewrite(plan) {
|
|
17
|
+
switch (plan.kind) {
|
|
18
|
+
case 'Filter': {
|
|
19
|
+
const child = rewrite(plan.input);
|
|
20
|
+
if (child.kind === 'Scan') {
|
|
21
|
+
const merged = child.pushedFilter
|
|
22
|
+
? { kind: 'logical', op: 'and', args: [child.pushedFilter, plan.predicate] }
|
|
23
|
+
: plan.predicate;
|
|
24
|
+
return { ...child, pushedFilter: merged };
|
|
25
|
+
}
|
|
26
|
+
return { ...plan, input: child };
|
|
27
|
+
}
|
|
28
|
+
case 'Project':
|
|
29
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
30
|
+
case 'Sort':
|
|
31
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
32
|
+
case 'Limit':
|
|
33
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
34
|
+
case 'Distinct':
|
|
35
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
36
|
+
case 'Aggregate':
|
|
37
|
+
// Recurse into input so WHERE filters below get pushed to the scan.
|
|
38
|
+
// HAVING (a Filter above us) must NOT be pushed through the Aggregate.
|
|
39
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
40
|
+
case 'Join':
|
|
41
|
+
// Per-table WHERE conjuncts were attached as Filters directly above each
|
|
42
|
+
// base scan by logical/build; push them in on both sides.
|
|
43
|
+
return { ...plan, left: rewrite(plan.left), right: rewrite(plan.right) };
|
|
44
|
+
default:
|
|
45
|
+
return plan;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=predicatePushdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicatePushdown.js","sourceRoot":"","sources":["../../../../sqlEngine/optimizer/rules/predicatePushdown.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAIH,8CAEC;AAFD,SAAgB,iBAAiB,CAAC,IAAiB;IAClD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,OAAO,CAAC,IAAiB;IACjC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACf,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY;oBAChC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,EAAE,EAAE,KAAc,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE;oBAC9F,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;gBAClB,OAAO,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;YAC3C,CAAC;YACD,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAClC,CAAC;QACD,KAAK,SAAS;YACb,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,MAAM;YACV,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,OAAO;YACX,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,UAAU;YACd,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,WAAW;YACf,oEAAoE;YACpE,uEAAuE;YACvE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,MAAM;YACV,yEAAyE;YACzE,0DAA0D;YAC1D,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1E;YACC,OAAO,IAAI,CAAC;IACd,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule R2: projection pushdown.
|
|
3
|
+
*
|
|
4
|
+
* Walks the plan from the root and computes the minimal set of attributes
|
|
5
|
+
* each Scan must produce. The set is stored on Scan.projection and is mapped
|
|
6
|
+
* to Resource API target.select by the physical planner.
|
|
7
|
+
*
|
|
8
|
+
* If any projection includes a `*`, we leave the scan's projection
|
|
9
|
+
* unrestricted (undefined → fetch all attributes). This keeps semantics
|
|
10
|
+
* matching legacy SQL. We can tighten this later by binding `*` to the
|
|
11
|
+
* concrete attribute list at plan time.
|
|
12
|
+
*/
|
|
13
|
+
import type { ExprNode, ProjectionNode, SortNode } from '../../parser/ast.ts';
|
|
14
|
+
import type { LogicalPlan } from '../../logical/op.ts';
|
|
15
|
+
export declare function projectionPushdown(plan: LogicalPlan): LogicalPlan | null;
|
|
16
|
+
declare function containsStar(projections: ProjectionNode[]): boolean;
|
|
17
|
+
declare function collectColumns(expr: ExprNode, out: Set<string>): void;
|
|
18
|
+
export declare const _internal: {
|
|
19
|
+
collectColumns: typeof collectColumns;
|
|
20
|
+
containsStar: typeof containsStar;
|
|
21
|
+
};
|
|
22
|
+
export declare function collectRequiredColumnsFromKeys(keys: SortNode[]): Set<string>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rule R2: projection pushdown.
|
|
4
|
+
*
|
|
5
|
+
* Walks the plan from the root and computes the minimal set of attributes
|
|
6
|
+
* each Scan must produce. The set is stored on Scan.projection and is mapped
|
|
7
|
+
* to Resource API target.select by the physical planner.
|
|
8
|
+
*
|
|
9
|
+
* If any projection includes a `*`, we leave the scan's projection
|
|
10
|
+
* unrestricted (undefined → fetch all attributes). This keeps semantics
|
|
11
|
+
* matching legacy SQL. We can tighten this later by binding `*` to the
|
|
12
|
+
* concrete attribute list at plan time.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports._internal = void 0;
|
|
16
|
+
exports.projectionPushdown = projectionPushdown;
|
|
17
|
+
exports.collectRequiredColumnsFromKeys = collectRequiredColumnsFromKeys;
|
|
18
|
+
function projectionPushdown(plan) {
|
|
19
|
+
return rewrite(plan, undefined);
|
|
20
|
+
}
|
|
21
|
+
function rewrite(plan, required) {
|
|
22
|
+
switch (plan.kind) {
|
|
23
|
+
case 'Project': {
|
|
24
|
+
if (containsStar(plan.projections)) {
|
|
25
|
+
return { ...plan, input: rewrite(plan.input, undefined) };
|
|
26
|
+
}
|
|
27
|
+
const need = new Set();
|
|
28
|
+
for (const p of plan.projections)
|
|
29
|
+
collectColumns(p.expr, need);
|
|
30
|
+
return { ...plan, input: rewrite(plan.input, need) };
|
|
31
|
+
}
|
|
32
|
+
case 'Filter': {
|
|
33
|
+
if (required) {
|
|
34
|
+
const merged = new Set(required);
|
|
35
|
+
collectColumns(plan.predicate, merged);
|
|
36
|
+
return { ...plan, input: rewrite(plan.input, merged) };
|
|
37
|
+
}
|
|
38
|
+
return { ...plan, input: rewrite(plan.input, undefined) };
|
|
39
|
+
}
|
|
40
|
+
case 'Sort': {
|
|
41
|
+
if (required) {
|
|
42
|
+
const merged = new Set(required);
|
|
43
|
+
for (const k of plan.keys)
|
|
44
|
+
collectColumns(k.expr, merged);
|
|
45
|
+
return { ...plan, input: rewrite(plan.input, merged) };
|
|
46
|
+
}
|
|
47
|
+
return { ...plan, input: rewrite(plan.input, undefined) };
|
|
48
|
+
}
|
|
49
|
+
case 'Limit':
|
|
50
|
+
return { ...plan, input: rewrite(plan.input, required) };
|
|
51
|
+
case 'Distinct':
|
|
52
|
+
return { ...plan, input: rewrite(plan.input, required) };
|
|
53
|
+
case 'Aggregate': {
|
|
54
|
+
// `required` from above contains synthetic __agg_N__ names that don't
|
|
55
|
+
// exist in the input. Compute the actual columns the input must supply.
|
|
56
|
+
const need = new Set();
|
|
57
|
+
for (const k of plan.groupKeys)
|
|
58
|
+
collectColumns(k, need);
|
|
59
|
+
for (const a of plan.aggs) {
|
|
60
|
+
if (a.arg.kind !== 'star')
|
|
61
|
+
collectColumns(a.arg, need);
|
|
62
|
+
}
|
|
63
|
+
return { ...plan, input: rewrite(plan.input, need.size > 0 ? need : undefined) };
|
|
64
|
+
}
|
|
65
|
+
case 'Scan': {
|
|
66
|
+
if (!required)
|
|
67
|
+
return { ...plan, projection: undefined };
|
|
68
|
+
if (plan.pushedFilter)
|
|
69
|
+
collectColumns(plan.pushedFilter, required);
|
|
70
|
+
if (plan.pushedSort)
|
|
71
|
+
for (const k of plan.pushedSort)
|
|
72
|
+
collectColumns(k.expr, required);
|
|
73
|
+
if (plan.residualFilter)
|
|
74
|
+
collectColumns(plan.residualFilter, required);
|
|
75
|
+
return { ...plan, projection: [...required].sort() };
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
return plan;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function containsStar(projections) {
|
|
82
|
+
for (const p of projections) {
|
|
83
|
+
if (containsStarExpr(p.expr))
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
function containsStarExpr(expr) {
|
|
89
|
+
if (expr.kind === 'star')
|
|
90
|
+
return true;
|
|
91
|
+
if (expr.kind === 'binop')
|
|
92
|
+
return containsStarExpr(expr.left) || containsStarExpr(expr.right);
|
|
93
|
+
if (expr.kind === 'logical')
|
|
94
|
+
return expr.args.some(containsStarExpr);
|
|
95
|
+
if (expr.kind === 'funcCall')
|
|
96
|
+
return expr.args.some(containsStarExpr);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
function collectColumns(expr, out) {
|
|
100
|
+
switch (expr.kind) {
|
|
101
|
+
case 'column':
|
|
102
|
+
out.add(expr.name);
|
|
103
|
+
return;
|
|
104
|
+
case 'binop':
|
|
105
|
+
collectColumns(expr.left, out);
|
|
106
|
+
collectColumns(expr.right, out);
|
|
107
|
+
return;
|
|
108
|
+
case 'logical':
|
|
109
|
+
expr.args.forEach((a) => collectColumns(a, out));
|
|
110
|
+
return;
|
|
111
|
+
case 'in':
|
|
112
|
+
collectColumns(expr.expr, out);
|
|
113
|
+
if (Array.isArray(expr.list)) {
|
|
114
|
+
expr.list.forEach((e) => collectColumns(e, out));
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
case 'between':
|
|
118
|
+
collectColumns(expr.expr, out);
|
|
119
|
+
collectColumns(expr.low, out);
|
|
120
|
+
collectColumns(expr.high, out);
|
|
121
|
+
return;
|
|
122
|
+
case 'like':
|
|
123
|
+
collectColumns(expr.expr, out);
|
|
124
|
+
collectColumns(expr.pattern, out);
|
|
125
|
+
return;
|
|
126
|
+
case 'isNull':
|
|
127
|
+
collectColumns(expr.expr, out);
|
|
128
|
+
return;
|
|
129
|
+
case 'funcCall':
|
|
130
|
+
expr.args.forEach((a) => collectColumns(a, out));
|
|
131
|
+
return;
|
|
132
|
+
case 'aggCall':
|
|
133
|
+
if (expr.arg.kind !== 'star')
|
|
134
|
+
collectColumns(expr.arg, out);
|
|
135
|
+
return;
|
|
136
|
+
case 'case':
|
|
137
|
+
for (const c of expr.cases) {
|
|
138
|
+
collectColumns(c.when, out);
|
|
139
|
+
collectColumns(c.then, out);
|
|
140
|
+
}
|
|
141
|
+
if (expr.else)
|
|
142
|
+
collectColumns(expr.else, out);
|
|
143
|
+
return;
|
|
144
|
+
case 'star':
|
|
145
|
+
case 'literal':
|
|
146
|
+
return;
|
|
147
|
+
default:
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Surface a helper for unit tests / external rules.
|
|
152
|
+
exports._internal = { collectColumns, containsStar };
|
|
153
|
+
// Helper that other rules can use without re-walking plans.
|
|
154
|
+
function collectRequiredColumnsFromKeys(keys) {
|
|
155
|
+
const out = new Set();
|
|
156
|
+
for (const k of keys)
|
|
157
|
+
collectColumns(k.expr, out);
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=projectionPushdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectionPushdown.js","sourceRoot":"","sources":["../../../../sqlEngine/optimizer/rules/projectionPushdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule R7: sort-from-index detection.
|
|
3
|
+
*
|
|
4
|
+
* If a Sort's keys all reference indexed attributes on a leaf Scan and the
|
|
5
|
+
* Sort sits directly above the Scan (with no Filter that would change row
|
|
6
|
+
* order), push the sort into Scan.pushedSort so Table.search returns rows in
|
|
7
|
+
* the requested order. The Resource API only supports a single primary sort
|
|
8
|
+
* with an optional `next` chain; phase 1 only handles the single-key case.
|
|
9
|
+
*
|
|
10
|
+
* If pushdown is not possible the Sort stays in place and the executor sorts
|
|
11
|
+
* in memory (capped by config.maxSortRows).
|
|
12
|
+
*/
|
|
13
|
+
import type { LogicalPlan } from '../../logical/op.ts';
|
|
14
|
+
export declare function sortFromIndex(plan: LogicalPlan): LogicalPlan | null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rule R7: sort-from-index detection.
|
|
4
|
+
*
|
|
5
|
+
* If a Sort's keys all reference indexed attributes on a leaf Scan and the
|
|
6
|
+
* Sort sits directly above the Scan (with no Filter that would change row
|
|
7
|
+
* order), push the sort into Scan.pushedSort so Table.search returns rows in
|
|
8
|
+
* the requested order. The Resource API only supports a single primary sort
|
|
9
|
+
* with an optional `next` chain; phase 1 only handles the single-key case.
|
|
10
|
+
*
|
|
11
|
+
* If pushdown is not possible the Sort stays in place and the executor sorts
|
|
12
|
+
* in memory (capped by config.maxSortRows).
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.sortFromIndex = sortFromIndex;
|
|
16
|
+
function sortFromIndex(plan) {
|
|
17
|
+
return rewrite(plan);
|
|
18
|
+
}
|
|
19
|
+
function rewrite(plan) {
|
|
20
|
+
switch (plan.kind) {
|
|
21
|
+
case 'Sort': {
|
|
22
|
+
const child = rewrite(plan.input);
|
|
23
|
+
if (child.kind === 'Scan' && !child.pushedSort) {
|
|
24
|
+
if (plan.keys.length !== 1)
|
|
25
|
+
return { ...plan, input: child };
|
|
26
|
+
const key = plan.keys[0];
|
|
27
|
+
if (key.expr.kind !== 'column')
|
|
28
|
+
return { ...plan, input: child };
|
|
29
|
+
const attribute = key.expr.name;
|
|
30
|
+
const indexed = child.boundTable?.attributes.some((a) => a.name === attribute && a.indexed);
|
|
31
|
+
if (!indexed)
|
|
32
|
+
return { ...plan, input: child };
|
|
33
|
+
return { ...child, pushedSort: [key] };
|
|
34
|
+
}
|
|
35
|
+
return { ...plan, input: child };
|
|
36
|
+
}
|
|
37
|
+
case 'Project':
|
|
38
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
39
|
+
case 'Filter':
|
|
40
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
41
|
+
case 'Limit':
|
|
42
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
43
|
+
case 'Distinct':
|
|
44
|
+
return { ...plan, input: rewrite(plan.input) };
|
|
45
|
+
default:
|
|
46
|
+
return plan;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=sortFromIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortFromIndex.js","sourceRoot":"","sources":["../../../../sqlEngine/optimizer/rules/sortFromIndex.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAIH,sCAEC;AAFD,SAAgB,aAAa,CAAC,IAAiB;IAC9C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,OAAO,CAAC,IAAiB;IACjC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,MAAM,CAAC,CAAC,CAAC;YACb,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;gBAChD,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;gBAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACzB,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;gBACjE,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;gBAChC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC5F,IAAI,CAAC,OAAO;oBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;gBAC/C,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;YACxC,CAAC;YACD,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAClC,CAAC;QACD,KAAK,SAAS;YACb,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,QAAQ;YACZ,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,OAAO;YACX,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,UAAU;YACd,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD;YACC,OAAO,IAAI,CAAC;IACd,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule R8: scan validation.
|
|
3
|
+
*
|
|
4
|
+
* After all pushdowns, we walk every Scan in the plan and check that the
|
|
5
|
+
* Resource API will accept the resulting target. Specifically: if no usable
|
|
6
|
+
* index condition was pushed (and `allowFullScan` is off in config), we
|
|
7
|
+
* throw EngineUnsupportedError naming the offending scan so the router can
|
|
8
|
+
* fall back to legacy in 'auto' mode.
|
|
9
|
+
*
|
|
10
|
+
* Phase 1 keeps the check loose: any pushedFilter that maps to at least one
|
|
11
|
+
* Resource API condition with an indexed attribute is sufficient. Filtering
|
|
12
|
+
* decisions sit in optimizer/whereToConditions.ts.
|
|
13
|
+
*/
|
|
14
|
+
import type { LogicalPlan } from '../../logical/op.ts';
|
|
15
|
+
export declare function validateScannable(plan: LogicalPlan): LogicalPlan | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rule R8: scan validation.
|
|
4
|
+
*
|
|
5
|
+
* After all pushdowns, we walk every Scan in the plan and check that the
|
|
6
|
+
* Resource API will accept the resulting target. Specifically: if no usable
|
|
7
|
+
* index condition was pushed (and `allowFullScan` is off in config), we
|
|
8
|
+
* throw EngineUnsupportedError naming the offending scan so the router can
|
|
9
|
+
* fall back to legacy in 'auto' mode.
|
|
10
|
+
*
|
|
11
|
+
* Phase 1 keeps the check loose: any pushedFilter that maps to at least one
|
|
12
|
+
* Resource API condition with an indexed attribute is sufficient. Filtering
|
|
13
|
+
* decisions sit in optimizer/whereToConditions.ts.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.validateScannable = validateScannable;
|
|
17
|
+
const errors_ts_1 = require("../../errors.js");
|
|
18
|
+
const whereToConditions_ts_1 = require("../whereToConditions.js");
|
|
19
|
+
const config_ts_1 = require("../../config.js");
|
|
20
|
+
function validateScannable(plan) {
|
|
21
|
+
const cfg = (0, config_ts_1.getSqlEngineConfig)();
|
|
22
|
+
walk(plan, cfg.allowFullScan);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function walk(plan, allowFullScan) {
|
|
26
|
+
if (plan.kind === 'Scan') {
|
|
27
|
+
validateScan(plan, allowFullScan);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if ('input' in plan)
|
|
31
|
+
walk(plan.input, allowFullScan);
|
|
32
|
+
if ('left' in plan)
|
|
33
|
+
walk(plan.left, allowFullScan);
|
|
34
|
+
if ('right' in plan)
|
|
35
|
+
walk(plan.right, allowFullScan);
|
|
36
|
+
}
|
|
37
|
+
function validateScan(scan, allowFullScan) {
|
|
38
|
+
if (allowFullScan)
|
|
39
|
+
return;
|
|
40
|
+
// Inner side of an index-nested-loop join: served by a per-outer-row indexed
|
|
41
|
+
// equality probe, not a standalone scan.
|
|
42
|
+
if (scan.joinProbe)
|
|
43
|
+
return;
|
|
44
|
+
// whereToConditions, given the table's attributes, pushes only conditions on
|
|
45
|
+
// indexed attributes (unindexed predicates are residualized into a post-scan
|
|
46
|
+
// Filter). The scan is valid only if at least one pushed condition can drive
|
|
47
|
+
// an index seek/range — a lone full-scan comparator (e.g. LIKE→contains) or a
|
|
48
|
+
// pushed sort is treated by Table.search as needFullScan and rejected under
|
|
49
|
+
// allowFullScan:false, so reject here too and let 'auto' fall back to legacy.
|
|
50
|
+
const { conditions } = (0, whereToConditions_ts_1.whereToConditions)(scan.pushedFilter, scan.boundTable?.attributes);
|
|
51
|
+
if (conditions.some((c) => (0, whereToConditions_ts_1.conditionUsesIndex)(c, scan.boundTable?.attributes)))
|
|
52
|
+
return;
|
|
53
|
+
// A sort pushed onto an indexed attribute drives the scan via the index's
|
|
54
|
+
// natural order (D-219): rows stream ordered from the index and a pushed LIMIT
|
|
55
|
+
// early-terminates, so it's a valid scan even with no index-driving predicate.
|
|
56
|
+
if ((0, whereToConditions_ts_1.sortDrivesIndex)(scan.pushedSort, scan.boundTable?.attributes))
|
|
57
|
+
return;
|
|
58
|
+
throw new errors_ts_1.EngineUnsupportedError(`scan on "${scan.table.database}.${scan.table.table}" has no usable index condition`, scan.pushedFilter);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=validateScannable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateScannable.js","sourceRoot":"","sources":["../../../../sqlEngine/optimizer/rules/validateScannable.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAOH,8CAIC;AARD,+CAAyD;AACzD,kEAAiG;AACjG,+CAAqD;AAErD,SAAgB,iBAAiB,CAAC,IAAiB;IAClD,MAAM,GAAG,GAAG,IAAA,8BAAkB,GAAE,CAAC;IACjC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAC,IAAiB,EAAE,aAAsB;IACtD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,YAAY,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAClC,OAAO;IACR,CAAC;IACD,IAAI,OAAO,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACrD,IAAI,MAAM,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACnD,IAAI,OAAO,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,YAAY,CAAC,IAAiB,EAAE,aAAsB;IAC9D,IAAI,aAAa;QAAE,OAAO;IAC1B,6EAA6E;IAC7E,yCAAyC;IACzC,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO;IAC3B,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,EAAE,UAAU,EAAE,GAAG,IAAA,wCAAiB,EAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACzF,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,yCAAkB,EAAC,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAAE,OAAO;IACvF,0EAA0E;IAC1E,+EAA+E;IAC/E,+EAA+E;IAC/E,IAAI,IAAA,sCAAe,EAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC;QAAE,OAAO;IAC1E,MAAM,IAAI,kCAAsB,CAC/B,YAAY,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,iCAAiC,EACpF,IAAI,CAAC,YAAY,CACjB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a (normalized) WHERE predicate into a Resource API condition tree
|
|
3
|
+
* plus an optional residual expression that the engine must apply as a
|
|
4
|
+
* post-scan Filter.
|
|
5
|
+
*
|
|
6
|
+
* The Resource API condition shape (see core/resources/search.ts:581):
|
|
7
|
+
* { attribute, value, comparator? }
|
|
8
|
+
* { conditions: Condition[], operator: 'and'|'or' }
|
|
9
|
+
*
|
|
10
|
+
* Comparator names use the canonical forms in
|
|
11
|
+
* core/resources/search.ts:204 (equals, ne, lt, le, gt, ge, between, gele,
|
|
12
|
+
* gelt, gtlt, gtle, starts_with, ends_with, contains).
|
|
13
|
+
*
|
|
14
|
+
* Anything that can't be represented as a condition (e.g., comparing two
|
|
15
|
+
* columns, function calls in WHERE, OR mixed with non-attribute predicates)
|
|
16
|
+
* is left in the residual.
|
|
17
|
+
*/
|
|
18
|
+
import type { ExprNode } from '../parser/ast.ts';
|
|
19
|
+
export interface DirectCondition {
|
|
20
|
+
attribute?: string;
|
|
21
|
+
value?: unknown;
|
|
22
|
+
comparator?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CompoundCondition {
|
|
25
|
+
conditions: ConditionNode[];
|
|
26
|
+
operator: 'and' | 'or';
|
|
27
|
+
}
|
|
28
|
+
export type ConditionNode = DirectCondition | CompoundCondition;
|
|
29
|
+
export interface ConvertResult {
|
|
30
|
+
conditions: ConditionNode[];
|
|
31
|
+
operator: 'and' | 'or';
|
|
32
|
+
residual?: ExprNode;
|
|
33
|
+
}
|
|
34
|
+
export type IndexedAttribute = {
|
|
35
|
+
name: string;
|
|
36
|
+
indexed: boolean;
|
|
37
|
+
isPrimaryKey?: boolean;
|
|
38
|
+
indexNulls?: boolean;
|
|
39
|
+
};
|
|
40
|
+
export declare function whereToConditions(predicate: ExprNode | undefined, attributes?: IndexedAttribute[]): ConvertResult;
|
|
41
|
+
/**
|
|
42
|
+
* Whether a condition can be pushed into Table.search at all: every attribute it
|
|
43
|
+
* references must be indexed, because search runs searchByIndex per condition and
|
|
44
|
+
* throws on an unindexed attribute. Comparator-agnostic — a full-scan comparator
|
|
45
|
+
* on an indexed attribute is still pushable (it becomes a filter once another
|
|
46
|
+
* condition drives the index). Null-valued conditions (IS [NOT] NULL and the
|
|
47
|
+
* `!=`/NOT IN not-null guards) are pushable only when the index actually indexes
|
|
48
|
+
* nulls — search throws "not indexed for nulls" otherwise (F-145), even when a
|
|
49
|
+
* different condition drives the scan. Unpushable conditions become a residual
|
|
50
|
+
* Filter (the expression evaluator's three-valued logic keeps NULL exclusion
|
|
51
|
+
* correct on the residual path).
|
|
52
|
+
*/
|
|
53
|
+
export declare function conditionIsPushable(cond: ConditionNode, attributes: IndexedAttribute[] | undefined): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a condition can *drive* an index scan (seek/range), and therefore make
|
|
56
|
+
* the scan valid without a full-table traversal. Stricter than pushability:
|
|
57
|
+
* excludes full-scan comparators and unseekable inequalities. Shared with the R8
|
|
58
|
+
* validateScannable rule so scannability and pushdown decisions can't drift.
|
|
59
|
+
*/
|
|
60
|
+
export declare function conditionUsesIndex(cond: ConditionNode, attributes: IndexedAttribute[] | undefined): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Whether a pushed sort can itself drive an index-ordered scan: its primary key
|
|
63
|
+
* is a single column on the table's PRIMARY KEY. Table.search aligns such a sort
|
|
64
|
+
* to the index's natural order (resources/Table.ts — it adds a `comparator:
|
|
65
|
+
* 'sort'` condition), so the rows stream from the index already ordered and a
|
|
66
|
+
* pushed LIMIT early-terminates (O(window)) — no separate in-memory sort. Shared
|
|
67
|
+
* by the R8 validateScannable rule and the physical scan builder so scannability
|
|
68
|
+
* and the `allowFullScan` flag can't drift. NOTE: because Table.search flags the
|
|
69
|
+
* sort scan as `needFullScan`, the physical scan must pass `allowFullScan: true`
|
|
70
|
+
* for it — see physicalIndexScan.
|
|
71
|
+
*
|
|
72
|
+
* Restricted to the PRIMARY KEY, because only there does index order provably
|
|
73
|
+
* equal legacy's full-scan-then-stable-sort order. A non-PK secondary index
|
|
74
|
+
* diverges from legacy in two ways (D-219): (1) a DESC sort reverses the whole
|
|
75
|
+
* composite `[value, primaryKey]` entry, so rows tying on `value` come back in
|
|
76
|
+
* primary-key-DESC order, whereas legacy's stable sort preserves primary-key-ASC
|
|
77
|
+
* order for ties; and (2) the secondary index omits rows whose sort attribute is
|
|
78
|
+
* null (they were never indexed), whereas a legacy full scan includes them. The
|
|
79
|
+
* PK is unique (no ties) and always present (no null keys), so its ordered scan
|
|
80
|
+
* matches legacy in both directions. Secondary-index sort streaming can be a
|
|
81
|
+
* follow-up once those two divergences are handled.
|
|
82
|
+
*/
|
|
83
|
+
export declare function sortDrivesIndex(pushedSort: {
|
|
84
|
+
expr: ExprNode;
|
|
85
|
+
}[] | undefined, attributes: IndexedAttribute[] | undefined): boolean;
|