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  }