This repository has been archived by the owner on Feb 21, 2024. It is now read-only.
/
planwalker.go
366 lines (311 loc) · 11.4 KB
/
planwalker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
package planner
import (
"github.com/featurebasedb/featurebase/v3/sql3/planner/types"
)
// PlanVisitor visits nodes in the plan.
type PlanVisitor interface {
// VisitOperator method is invoked for each node during PlanWalk. If the
// resulting PlanVisitor is not nil, PlanWalk visits each of the children of
// the node with that visitor, followed by a call of VisitOperator(nil) to
// the returned visitor.
VisitOperator(op types.PlanOperator) PlanVisitor
}
// PlanWalk traverses the plan depth-first. It starts by calling
// v.VisitOperator; node must not be nil. If the result returned by
// v.VisitOperator is not nil, PlanWalk is invoked recursively with the returned
// result for each of the children of the plan operator, followed by a call of
// v.VisitOperator(nil) to the returned result. If v.VisitOperator(op) returns
// non-nil, then all children are walked, even if one of them returns nil.
func PlanWalk(v PlanVisitor, op types.PlanOperator) {
if v = v.VisitOperator(op); v == nil {
return
}
for _, child := range op.Children() {
PlanWalk(v, child)
}
v.VisitOperator(nil)
}
type planInspectionFunction func(types.PlanOperator) bool
func (f planInspectionFunction) VisitOperator(op types.PlanOperator) PlanVisitor {
if f(op) {
return f
}
return nil
}
// InspectPlan traverses the plan op graph depth-first order
// if f(op) returns true, InspectPlan invokes f recursively for each of the children of op,
// followed by a call of f(nil).
func InspectPlan(op types.PlanOperator, f planInspectionFunction) {
PlanWalk(f, op)
}
//-----------------------------------------------------------------------------
// ExprVisitor visits expressions in an expression tree.
type ExprVisitor interface {
// VisitExpr method is invoked for each expr encountered by ExprWalk.
// If the result is not nil, ExprWalk visits each of the children
// of the expr, followed by a call of VisitExpr(nil) to the returned result.
VisitExpr(expr types.PlanExpression) ExprVisitor
}
func ExprWalk(v ExprVisitor, expr types.PlanExpression) {
if v = v.VisitExpr(expr); v == nil {
return
}
for _, child := range expr.Children() {
ExprWalk(v, child)
}
v.VisitExpr(nil)
}
type exprInspector func(types.PlanExpression) bool
func (f exprInspector) VisitExpr(e types.PlanExpression) ExprVisitor {
if f(e) {
return f
}
return nil
}
// walkExpressions traverses the plan and calls ExprWalk on any expression it finds
func walkExpressions(v ExprVisitor, op types.PlanOperator) {
InspectPlan(op, func(operator types.PlanOperator) bool {
if n, ok := operator.(types.ContainsExpressions); ok {
for _, e := range n.Expressions() {
ExprWalk(v, e)
}
}
return true
})
}
// InspectOperatorExpressions traverses the plan and calls WalkExpressions on any
// expression it finds.
func InspectOperatorExpressions(op types.PlanOperator, f exprInspector) {
walkExpressions(f, op)
}
// InspectExpression traverses expressions in depth-first order
func InspectExpression(expr types.PlanExpression, f func(expr types.PlanExpression) bool) {
ExprWalk(exprInspector(f), expr)
}
//-----------------------------------------------------------------------------
// PlanOpExprVisitor visits expressions in an expression tree. Like ExprVisitor, but with the added context of the plan op in which
// an expression is embedded.
type PlanOpExprVisitor interface {
// VisitPlanOpExpr method is invoked for each expr encountered by Walk. If the result Visitor is not nil, Walk visits each of
// the children of the expr with that visitor, followed by a call of VisitPlanOpExpr(nil, nil) to the returned visitor.
VisitPlanOpExpr(op types.PlanOperator, expression types.PlanExpression) PlanOpExprVisitor
}
// ExprWithPlanOpWalk traverses the expression tree in depth-first order. It starts by calling v.VisitPlanOpExpr(op, expr); expr must
// not be nil. If the visitor returned by v.VisitPlanOpExpr(op, expr) is not nil, Walk is invoked recursively with the returned
// visitor for each children of the expr, followed by a call of v.VisitPlanOpExpr(nil, nil) to the returned visitor.
func ExprWithPlanOpWalk(v PlanOpExprVisitor, op types.PlanOperator, expr types.PlanExpression) {
if v = v.VisitPlanOpExpr(op, expr); v == nil {
return
}
for _, child := range expr.Children() {
ExprWithPlanOpWalk(v, op, child)
}
v.VisitPlanOpExpr(nil, nil)
}
type exprWithNodeInspector func(types.PlanOperator, types.PlanExpression) bool
func (f exprWithNodeInspector) VisitPlanOpExpr(op types.PlanOperator, expr types.PlanExpression) PlanOpExprVisitor {
if f(op, expr) {
return f
}
return nil
}
// WalkExpressionsWithPlanOp traverses the plan and calls ExprWithPlanOpWalk on any expression it finds.
func WalkExpressionsWithPlanOp(v PlanOpExprVisitor, op types.PlanOperator) {
InspectPlan(op, func(operator types.PlanOperator) bool {
if expressioner, ok := operator.(types.ContainsExpressions); ok {
for _, e := range expressioner.Expressions() {
ExprWithPlanOpWalk(v, operator, e)
}
}
return true
})
}
// InspectExpressionsWithPlanOp traverses the plan and calls f on any expression it finds.
func InspectExpressionsWithPlanOp(op types.PlanOperator, f exprWithNodeInspector) {
WalkExpressionsWithPlanOp(f, op)
}
// PlanOpTransformFunc is a function that given a plan op will return either a transformed plan op or the original plan op.
// If there was a transformation, the bool will be true, and an error if there was an error
type PlanOpTransformFunc func(op types.PlanOperator) (types.PlanOperator, bool, error)
// TransformPlanOp applies a depth first transformation function to the given plan op
// It returns a tuple that is the result of the transformation; the new PlanOperator, a bool that
// is true if the resultant PlanOperator has not been transformed or an error.
// If the TransformPlanOp has children it will iterate the children and call the transformation
// function on each of them in turn. If those operators are transformed, it will create a new operator
// with those children. The last step is to call the transformation on the passed PlanOperator
func TransformPlanOp(op types.PlanOperator, f PlanOpTransformFunc) (types.PlanOperator, bool, error) {
thisOperator := op
children := thisOperator.Children()
if len(children) == 0 {
return f(thisOperator)
}
var newChildren []types.PlanOperator
for i := range children {
child := children[i]
child, same, err := TransformPlanOp(child, f)
if err != nil {
return nil, true, err
}
if !same {
if newChildren == nil {
newChildren = make([]types.PlanOperator, len(children))
copy(newChildren, children)
}
newChildren[i] = child
}
}
var err error
sameChildren := true
if len(newChildren) > 0 {
sameChildren = false
thisOperator, err = thisOperator.WithChildren(newChildren...)
if err != nil {
return nil, true, err
}
}
resultOperator, sameOperator, err := f(thisOperator)
if err != nil {
return nil, true, err
}
return resultOperator, sameChildren && sameOperator, nil
}
// ParentContext is a struct that enables transformation functions to include a parent operator
type ParentContext struct {
Operator types.PlanOperator
Parent types.PlanOperator
ChildCount int
}
type ParentContextFunc func(c ParentContext) (types.PlanOperator, bool, error)
type ParentSelectorFunc func(c ParentContext) bool
// TransformPlanOpWithParent applies a transformation function to a plan operator in the context that plan operators parent
func TransformPlanOpWithParent(op types.PlanOperator, s ParentSelectorFunc, f ParentContextFunc) (types.PlanOperator, bool, error) {
return planOpWithParentHelper(ParentContext{op, nil, -1}, s, f)
}
func planOpWithParentHelper(c ParentContext, s ParentSelectorFunc, f ParentContextFunc) (types.PlanOperator, bool, error) {
operator := c.Operator
children := operator.Children()
if len(children) == 0 {
return f(c)
}
var (
newChildren []types.PlanOperator
err error
)
for i := range children {
child := children[i]
cc := ParentContext{child, operator, i}
if s == nil || s(cc) {
child, same, err := planOpWithParentHelper(cc, s, f)
if err != nil {
return nil, true, err
}
if !same {
if newChildren == nil {
newChildren = make([]types.PlanOperator, len(children))
copy(newChildren, children)
}
newChildren[i] = child
}
}
}
sameChildren := true
if len(newChildren) > 0 {
sameChildren = false
operator, err = operator.WithChildren(newChildren...)
if err != nil {
return nil, true, err
}
}
resultOperator, sameOperator, err := f(ParentContext{operator, c.Parent, c.ChildCount})
if err != nil {
return nil, true, err
}
return resultOperator, sameChildren && sameOperator, nil
}
// ExprWithPlanOpFunc is a function that given an expression and the node
// that contains it, will return that expression as is or transformed
// along with an error, if any.
type ExprWithPlanOpFunc func(op types.PlanOperator, expr types.PlanExpression) (types.PlanExpression, bool, error)
// ExprFunc is a function that given an expression will return that
// expression as is or transformed, or bool to indicate
// whether the expression was modified, and an error or nil.
type ExprFunc func(expr types.PlanExpression) (types.PlanExpression, bool, error)
// ExprSelectorFunc is a function that can be used as a filter selector during
// expression transformation - it is called before calling a transformation
// function on a child expression; if it returns false, the child is skipped
type ExprSelectorFunc func(parentExpr, childExpr types.PlanExpression) bool
// TransformSinglePlanOpExpressions applies a transformation function to all expressions on the given plan operator
func TransformSinglePlanOpExpressions(op types.PlanOperator, f ExprFunc, selector ExprSelectorFunc) (types.PlanOperator, bool, error) {
e, ok := op.(types.ContainsExpressions)
if !ok {
return op, true, nil
}
exprs := e.Expressions()
if len(exprs) == 0 {
return op, true, nil
}
var newExprs []types.PlanExpression
for i := range exprs {
expr := exprs[i]
expr, same, err := TransformExpr(expr, f, selector)
if err != nil {
return nil, true, err
}
if !same {
if newExprs == nil {
newExprs = make([]types.PlanExpression, len(exprs))
copy(newExprs, exprs)
}
newExprs[i] = expr
}
}
if len(newExprs) > 0 {
n, err := e.WithUpdatedExpressions(newExprs...)
if err != nil {
return nil, true, err
}
return n, false, nil
}
return op, true, nil
}
// TransformExpr applies a depth first transformation function to an expression
func TransformExpr(expr types.PlanExpression, transformFunction ExprFunc, selector ExprSelectorFunc) (types.PlanExpression, bool, error) {
thisExpr := expr
children := expr.Children()
if len(children) == 0 {
return transformFunction(thisExpr)
}
var (
newChildren []types.PlanExpression
err error
)
for i := 0; i < len(children); i++ {
child := children[i]
if selector(expr, child) {
c, same, err := TransformExpr(child, transformFunction, selector)
if err != nil {
return nil, true, err
}
if !same {
if newChildren == nil {
newChildren = make([]types.PlanExpression, len(children))
copy(newChildren, children)
}
newChildren[i] = c
}
}
}
sameChildren := true
if len(newChildren) > 0 {
sameChildren = false
thisExpr, err = thisExpr.WithChildren(newChildren...)
if err != nil {
return nil, true, err
}
}
resultExpr, sameExpr, err := transformFunction(thisExpr)
if err != nil {
return nil, true, err
}
return resultExpr, sameChildren && sameExpr, nil
}