vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/projection.go (about) 1 /* 2 Copyright 2022 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package engine 18 19 import ( 20 "context" 21 "sync" 22 23 "vitess.io/vitess/go/sqltypes" 24 querypb "vitess.io/vitess/go/vt/proto/query" 25 "vitess.io/vitess/go/vt/vtgate/evalengine" 26 ) 27 28 var _ Primitive = (*Projection)(nil) 29 30 // Projection can evaluate expressions and project the results 31 type Projection struct { 32 Cols []string 33 Exprs []evalengine.Expr 34 Input Primitive 35 noTxNeeded 36 } 37 38 // RouteType implements the Primitive interface 39 func (p *Projection) RouteType() string { 40 return p.Input.RouteType() 41 } 42 43 // GetKeyspaceName implements the Primitive interface 44 func (p *Projection) GetKeyspaceName() string { 45 return p.Input.GetKeyspaceName() 46 } 47 48 // GetTableName implements the Primitive interface 49 func (p *Projection) GetTableName() string { 50 return p.Input.GetTableName() 51 } 52 53 // TryExecute implements the Primitive interface 54 func (p *Projection) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { 55 result, err := vcursor.ExecutePrimitive(ctx, p.Input, bindVars, wantfields) 56 if err != nil { 57 return nil, err 58 } 59 60 env := evalengine.EnvWithBindVars(bindVars, vcursor.ConnCollation()) 61 env.Fields = result.Fields 62 var resultRows []sqltypes.Row 63 for _, row := range result.Rows { 64 resultRow := make(sqltypes.Row, 0, len(p.Exprs)) 65 env.Row = row 66 for _, exp := range p.Exprs { 67 result, err := env.Evaluate(exp) 68 if err != nil { 69 return nil, err 70 } 71 resultRow = append(resultRow, result.Value()) 72 } 73 resultRows = append(resultRows, resultRow) 74 } 75 if wantfields { 76 err := p.addFields(env, result) 77 if err != nil { 78 return nil, err 79 } 80 } 81 result.Rows = resultRows 82 return result, nil 83 } 84 85 // TryStreamExecute implements the Primitive interface 86 func (p *Projection) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { 87 env := evalengine.EnvWithBindVars(bindVars, vcursor.ConnCollation()) 88 var once sync.Once 89 var fields []*querypb.Field 90 return vcursor.StreamExecutePrimitive(ctx, p.Input, bindVars, wantfields, func(qr *sqltypes.Result) error { 91 var err error 92 if wantfields { 93 once.Do(func() { 94 env.Fields = qr.Fields 95 fieldRes := &sqltypes.Result{} 96 err = p.addFields(env, fieldRes) 97 if err != nil { 98 return 99 } 100 fields = fieldRes.Fields 101 err = callback(fieldRes) 102 if err != nil { 103 return 104 } 105 }) 106 qr.Fields = fields 107 } 108 if err != nil { 109 return err 110 } 111 resultRows := make([]sqltypes.Row, 0, len(qr.Rows)) 112 for _, r := range qr.Rows { 113 resultRow := make(sqltypes.Row, 0, len(p.Exprs)) 114 env.Row = r 115 for _, exp := range p.Exprs { 116 c, err := env.Evaluate(exp) 117 if err != nil { 118 return err 119 } 120 resultRow = append(resultRow, c.Value()) 121 } 122 resultRows = append(resultRows, resultRow) 123 } 124 qr.Rows = resultRows 125 return callback(qr) 126 }) 127 } 128 129 // GetFields implements the Primitive interface 130 func (p *Projection) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 131 qr, err := p.Input.GetFields(ctx, vcursor, bindVars) 132 if err != nil { 133 return nil, err 134 } 135 env := evalengine.EnvWithBindVars(bindVars, vcursor.ConnCollation()) 136 err = p.addFields(env, qr) 137 if err != nil { 138 return nil, err 139 } 140 return qr, nil 141 } 142 143 func (p *Projection) addFields(env *evalengine.ExpressionEnv, qr *sqltypes.Result) error { 144 qr.Fields = nil 145 for i, col := range p.Cols { 146 q, err := env.TypeOf(p.Exprs[i]) 147 if err != nil { 148 return err 149 } 150 qr.Fields = append(qr.Fields, &querypb.Field{ 151 Name: col, 152 Type: q, 153 }) 154 } 155 return nil 156 } 157 158 // Inputs implements the Primitive interface 159 func (p *Projection) Inputs() []Primitive { 160 return []Primitive{p.Input} 161 } 162 163 // description implements the Primitive interface 164 func (p *Projection) description() PrimitiveDescription { 165 var exprs []string 166 for idx, e := range p.Exprs { 167 expr := evalengine.FormatExpr(e) 168 alias := p.Cols[idx] 169 if alias != "" { 170 expr += " as " + alias 171 } 172 exprs = append(exprs, expr) 173 } 174 return PrimitiveDescription{ 175 OperatorType: "Projection", 176 Other: map[string]any{ 177 "Expressions": exprs, 178 }, 179 } 180 }