github.com/dolthub/go-mysql-server@v0.18.0/sql/analyzer/batch.go (about) 1 // Copyright 2020-2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package analyzer 16 17 import ( 18 "reflect" 19 20 "github.com/dolthub/go-mysql-server/sql" 21 "github.com/dolthub/go-mysql-server/sql/plan" 22 "github.com/dolthub/go-mysql-server/sql/transform" 23 ) 24 25 // RuleFunc is the function to be applied in a rule. 26 type RuleFunc func(*sql.Context, *Analyzer, sql.Node, *plan.Scope, RuleSelector) (sql.Node, transform.TreeIdentity, error) 27 28 // RuleSelector filters analysis rules by id 29 type RuleSelector func(RuleId) bool 30 31 // Rule to transform nodes. 32 type Rule struct { 33 // Name of the rule. 34 Id RuleId 35 // Apply transforms a node. 36 Apply RuleFunc 37 } 38 39 // BatchSelector filters analysis batches by name 40 type BatchSelector func(string) bool 41 42 // Batch executes a set of rules a specific number of times. 43 // When this number of times is reached, the actual node 44 // and ErrMaxAnalysisIters is returned. 45 type Batch struct { 46 Desc string 47 Iterations int 48 Rules []Rule 49 } 50 51 // Eval executes the rules of the batch. On any error, the partially transformed node is returned along with the error. 52 // If the batch's max number of iterations is reached without achieving stabilization (batch evaluation no longer 53 // changes the node), then this method returns ErrMaxAnalysisIters. 54 func (b *Batch) Eval(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, sel RuleSelector) (sql.Node, transform.TreeIdentity, error) { 55 return b.EvalWithSelector(ctx, a, n, scope, sel) 56 } 57 58 func (b *Batch) EvalWithSelector(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, sel RuleSelector) (sql.Node, transform.TreeIdentity, error) { 59 if b.Iterations == 0 { 60 return n, transform.SameTree, nil 61 } 62 a.PushDebugContext("0") 63 cur, _, err := b.evalOnce(ctx, a, n, scope, sel) 64 a.PopDebugContext() 65 if err != nil { 66 return cur, transform.SameTree, err 67 } 68 return cur, transform.NewTree, nil 69 } 70 71 // evalOnce returns the result of evaluating a batch of rules on the node given. In the result of an error, the result 72 // of the last successful transformation is returned along with the error. If no transformation was successful, the 73 // input node is returned as-is. 74 func (b *Batch) evalOnce(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, sel RuleSelector) (sql.Node, transform.TreeIdentity, error) { 75 var ( 76 same = transform.SameTree 77 allSame = transform.SameTree 78 next sql.Node 79 prev = n 80 ) 81 for _, rule := range b.Rules { 82 if !sel(rule.Id) { 83 a.Log("Skipping rule %s", rule.Id) 84 continue 85 } 86 var err error 87 a.Log("Evaluating rule %s", rule.Id) 88 a.PushDebugContext(rule.Id.String()) 89 next, same, err = rule.Apply(ctx, a, prev, scope, sel) 90 allSame = same && allSame 91 if next != nil && !same { 92 a.LogNode(next) 93 // We should only do this if the result has changed, but some rules currently misbehave and falsely report nothing 94 // changed 95 a.LogDiff(prev, next) 96 } 97 a.PopDebugContext() 98 if err != nil { 99 // Returning the last node before the error is important. This is non-idiomatic, but in the case of partial 100 // resolution before an error we want the last successful transformation result. Very important for resolving 101 // subqueries. 102 return prev, allSame, err 103 } 104 prev = next 105 } 106 107 return prev, allSame, nil 108 } 109 110 func nodesEqual(a, b sql.Node) bool { 111 if e, ok := a.(equaler); ok { 112 return e.Equal(b) 113 } 114 115 if e, ok := b.(equaler); ok { 116 return e.Equal(a) 117 } 118 119 return reflect.DeepEqual(a, b) 120 } 121 122 type equaler interface { 123 Equal(sql.Node) bool 124 }