vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/expression_converter.go (about)

     1  /*
     2  Copyright 2020 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 planbuilder
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    24  
    25  	"vitess.io/vitess/go/mysql/collations"
    26  	"vitess.io/vitess/go/vt/sqlparser"
    27  	"vitess.io/vitess/go/vt/vtgate/engine"
    28  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    29  )
    30  
    31  type expressionConverter struct {
    32  	tabletExpressions []sqlparser.Expr
    33  }
    34  
    35  func booleanValues(astExpr sqlparser.Expr) evalengine.Expr {
    36  	var (
    37  		ON  = evalengine.NewLiteralInt(1)
    38  		OFF = evalengine.NewLiteralInt(0)
    39  	)
    40  	switch node := astExpr.(type) {
    41  	case *sqlparser.Literal:
    42  		//set autocommit = 'on'
    43  		if node.Type == sqlparser.StrVal {
    44  			switch strings.ToLower(node.Val) {
    45  			case "on":
    46  				return ON
    47  			case "off":
    48  				return OFF
    49  			}
    50  		}
    51  	case *sqlparser.ColName:
    52  		//set autocommit = on
    53  		switch node.Name.Lowered() {
    54  		case "on":
    55  			return ON
    56  		case "off":
    57  			return OFF
    58  		}
    59  	}
    60  	return nil
    61  }
    62  
    63  func identifierAsStringValue(astExpr sqlparser.Expr) evalengine.Expr {
    64  	colName, isColName := astExpr.(*sqlparser.ColName)
    65  	if isColName {
    66  		// TODO@collations: proper collation for column name
    67  		return evalengine.NewLiteralString([]byte(colName.Name.Lowered()), collations.TypedCollation{})
    68  	}
    69  	return nil
    70  }
    71  
    72  func (ec *expressionConverter) convert(astExpr sqlparser.Expr, boolean, identifierAsString bool) (evalengine.Expr, error) {
    73  	if boolean {
    74  		evalExpr := booleanValues(astExpr)
    75  		if evalExpr != nil {
    76  			return evalExpr, nil
    77  		}
    78  	}
    79  	if identifierAsString {
    80  		evalExpr := identifierAsStringValue(astExpr)
    81  		if evalExpr != nil {
    82  			return evalExpr, nil
    83  		}
    84  	}
    85  	evalExpr, err := evalengine.Translate(astExpr, nil)
    86  	if err != nil {
    87  		if !strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) {
    88  			return nil, err
    89  		}
    90  		evalExpr = &evalengine.Column{Offset: len(ec.tabletExpressions)}
    91  		ec.tabletExpressions = append(ec.tabletExpressions, astExpr)
    92  	}
    93  	return evalExpr, nil
    94  }
    95  
    96  func (ec *expressionConverter) source(vschema plancontext.VSchema) (engine.Primitive, error) {
    97  	if len(ec.tabletExpressions) == 0 {
    98  		return &engine.SingleRow{}, nil
    99  	}
   100  	ks, dest, err := resolveDestination(vschema)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	var expr []string
   106  	for _, e := range ec.tabletExpressions {
   107  		expr = append(expr, sqlparser.String(e))
   108  	}
   109  	query := fmt.Sprintf("select %s from dual", strings.Join(expr, ","))
   110  
   111  	primitive := &engine.Send{
   112  		Keyspace:          ks,
   113  		TargetDestination: dest,
   114  		Query:             query,
   115  		IsDML:             false,
   116  		SingleShardOnly:   true,
   117  	}
   118  	return primitive, nil
   119  }