vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go (about)

     1  /*
     2  Copyright 2019 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 vstreamer
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"regexp"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"vitess.io/vitess/go/vt/vtgate/semantics"
    27  
    28  	"vitess.io/vitess/go/mysql/collations"
    29  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    30  
    31  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    32  	"vitess.io/vitess/go/vt/vterrors"
    33  
    34  	"vitess.io/vitess/go/vt/log"
    35  
    36  	"vitess.io/vitess/go/mysql"
    37  	"vitess.io/vitess/go/sqltypes"
    38  	"vitess.io/vitess/go/vt/key"
    39  	"vitess.io/vitess/go/vt/sqlparser"
    40  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    41  
    42  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    43  	querypb "vitess.io/vitess/go/vt/proto/query"
    44  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    45  )
    46  
    47  // Plan represents the plan for a table.
    48  type Plan struct {
    49  	Table *Table
    50  
    51  	// ColExprs is the list of column expressions to be sent
    52  	// in the stream.
    53  	ColExprs []ColExpr
    54  
    55  	convertUsingUTF8Columns map[string]bool
    56  
    57  	// Any columns that require a function expression in the
    58  	// stream.
    59  	columnFuncExprs map[string]*sqlparser.FuncExpr
    60  
    61  	// Filters is the list of filters to be applied to the columns
    62  	// of the table.
    63  	Filters []Filter
    64  }
    65  
    66  // Opcode enumerates the operators supported in a where clause
    67  type Opcode int
    68  
    69  const (
    70  	// Equal is used to filter a comparable column on a specific value
    71  	Equal = Opcode(iota)
    72  	// VindexMatch is used for an in_keyrange() construct
    73  	VindexMatch
    74  	// LessThan is used to filter a comparable column if < specific value
    75  	LessThan
    76  	// LessThanEqual is used to filter a comparable column if <= specific value
    77  	LessThanEqual
    78  	// GreaterThan is used to filter a comparable column if > specific value
    79  	GreaterThan
    80  	// GreaterThanEqual is used to filter a comparable column if >= specific value
    81  	GreaterThanEqual
    82  	// NotEqual is used to filter a comparable column if != specific value
    83  	NotEqual
    84  )
    85  
    86  // Filter contains opcodes for filtering.
    87  type Filter struct {
    88  	Opcode Opcode
    89  	ColNum int
    90  	Value  sqltypes.Value
    91  
    92  	// Parameters for VindexMatch.
    93  	// Vindex, VindexColumns and KeyRange, if set, will be used
    94  	// to filter the row.
    95  	// VindexColumns contains the column numbers of the table,
    96  	// and not the column numbers of the stream to be sent.
    97  	Vindex        vindexes.Vindex
    98  	VindexColumns []int
    99  	KeyRange      *topodatapb.KeyRange
   100  }
   101  
   102  // ColExpr represents a column expression.
   103  type ColExpr struct {
   104  	// ColNum specifies the source column value.
   105  	ColNum int
   106  
   107  	// Vindex and VindexColumns, if set, will be used to generate
   108  	// a keyspace_id. If so, ColNum is ignored.
   109  	// VindexColumns contains the column numbers of the table,
   110  	// and not the column numbers of the stream to be sent.
   111  	Vindex        vindexes.Vindex
   112  	VindexColumns []int
   113  
   114  	Field *querypb.Field
   115  
   116  	FixedValue sqltypes.Value
   117  }
   118  
   119  // Table contains the metadata for a table.
   120  type Table struct {
   121  	Name   string
   122  	Fields []*querypb.Field
   123  }
   124  
   125  // FindColumn finds a column in the table. It returns the index if found.
   126  // Otherwise, it returns -1.
   127  func (ta *Table) FindColumn(name sqlparser.IdentifierCI) int {
   128  	for i, col := range ta.Fields {
   129  		if name.EqualString(col.Name) {
   130  			return i
   131  		}
   132  	}
   133  	return -1
   134  }
   135  
   136  // fields returns the fields for the plan.
   137  func (plan *Plan) fields() []*querypb.Field {
   138  	fields := make([]*querypb.Field, len(plan.ColExprs))
   139  	for i, ce := range plan.ColExprs {
   140  		fields[i] = ce.Field
   141  	}
   142  	return fields
   143  }
   144  
   145  // getOpcode returns the equivalent planbuilder opcode for operators that are supported in Filters
   146  func getOpcode(comparison *sqlparser.ComparisonExpr) (Opcode, error) {
   147  	var opcode Opcode
   148  	switch comparison.Operator {
   149  	case sqlparser.EqualOp:
   150  		opcode = Equal
   151  	case sqlparser.LessThanOp:
   152  		opcode = LessThan
   153  	case sqlparser.LessEqualOp:
   154  		opcode = LessThanEqual
   155  	case sqlparser.GreaterThanOp:
   156  		opcode = GreaterThan
   157  	case sqlparser.GreaterEqualOp:
   158  		opcode = GreaterThanEqual
   159  	case sqlparser.NotEqualOp:
   160  		opcode = NotEqual
   161  	default:
   162  		return -1, fmt.Errorf("comparison operator %s not supported", comparison.Operator.ToString())
   163  	}
   164  	return opcode, nil
   165  }
   166  
   167  // compare returns true after applying the comparison specified in the Filter to the actual data in the column
   168  func compare(comparison Opcode, columnValue, filterValue sqltypes.Value, charset collations.ID) (bool, error) {
   169  	// use null semantics: return false if either value is null
   170  	if columnValue.IsNull() || filterValue.IsNull() {
   171  		return false, nil
   172  	}
   173  	// at this point neither values can be null
   174  	// NullsafeCompare returns 0 if values match, -1 if columnValue < filterValue, 1 if columnValue > filterValue
   175  	result, err := evalengine.NullsafeCompare(columnValue, filterValue, charset)
   176  	if err != nil {
   177  		return false, err
   178  	}
   179  
   180  	switch comparison {
   181  	case Equal:
   182  		if result == 0 {
   183  			return true, nil
   184  		}
   185  	case NotEqual:
   186  		if result != 0 {
   187  			return true, nil
   188  		}
   189  	case LessThan:
   190  		if result == -1 {
   191  			return true, nil
   192  		}
   193  	case LessThanEqual:
   194  		if result <= 0 {
   195  			return true, nil
   196  		}
   197  	case GreaterThan:
   198  		if result == 1 {
   199  			return true, nil
   200  		}
   201  	case GreaterThanEqual:
   202  		if result >= 0 {
   203  			return true, nil
   204  		}
   205  	default:
   206  		return false, fmt.Errorf("comparison operator %d not supported", comparison)
   207  	}
   208  	return false, nil
   209  }
   210  
   211  // filter filters the row against the plan. It returns false if the row did not match.
   212  // The output of the filtering operation is stored in the 'result' argument because
   213  // filtering cannot be performed in-place. The result argument must be a slice of
   214  // length equal to ColExprs
   215  func (plan *Plan) filter(values, result []sqltypes.Value, charsets []collations.ID) (bool, error) {
   216  	if len(result) != len(plan.ColExprs) {
   217  		return false, fmt.Errorf("expected %d values in result slice", len(plan.ColExprs))
   218  	}
   219  	for _, filter := range plan.Filters {
   220  		switch filter.Opcode {
   221  		case VindexMatch:
   222  			ksid, err := getKeyspaceID(values, filter.Vindex, filter.VindexColumns, plan.Table.Fields)
   223  			if err != nil {
   224  				return false, err
   225  			}
   226  			if !key.KeyRangeContains(filter.KeyRange, ksid) {
   227  				return false, nil
   228  			}
   229  		default:
   230  			match, err := compare(filter.Opcode, values[filter.ColNum], filter.Value, charsets[filter.ColNum])
   231  			if err != nil {
   232  				return false, err
   233  			}
   234  			if !match {
   235  				return false, nil
   236  			}
   237  		}
   238  	}
   239  	for i, colExpr := range plan.ColExprs {
   240  		if colExpr.ColNum == -1 {
   241  			result[i] = colExpr.FixedValue
   242  			continue
   243  		}
   244  		if colExpr.ColNum >= len(values) {
   245  			return false, fmt.Errorf("index out of range, colExpr.ColNum: %d, len(values): %d", colExpr.ColNum, len(values))
   246  		}
   247  		if colExpr.Vindex == nil {
   248  			result[i] = values[colExpr.ColNum]
   249  		} else {
   250  			ksid, err := getKeyspaceID(values, colExpr.Vindex, colExpr.VindexColumns, plan.Table.Fields)
   251  			if err != nil {
   252  				return false, err
   253  			}
   254  			result[i] = sqltypes.MakeTrusted(sqltypes.VarBinary, []byte(ksid))
   255  		}
   256  	}
   257  	return true, nil
   258  }
   259  
   260  func getKeyspaceID(values []sqltypes.Value, vindex vindexes.Vindex, vindexColumns []int, fields []*querypb.Field) (key.DestinationKeyspaceID, error) {
   261  	vindexValues := make([]sqltypes.Value, 0, len(vindexColumns))
   262  	for _, col := range vindexColumns {
   263  		vindexValues = append(vindexValues, values[col])
   264  	}
   265  	destinations, err := vindexes.Map(context.TODO(), vindex, nil, [][]sqltypes.Value{vindexValues})
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  	if len(destinations) != 1 {
   270  		return nil, fmt.Errorf("mapping row to keyspace id returned an invalid array of destinations: %v", key.DestinationsString(destinations))
   271  	}
   272  	ksid, ok := destinations[0].(key.DestinationKeyspaceID)
   273  	if !ok || len(ksid) == 0 {
   274  		return nil, fmt.Errorf("could not map %v to a keyspace id, got destination %v", vindexValues, destinations[0])
   275  	}
   276  	return ksid, nil
   277  }
   278  
   279  func mustSendStmt(query mysql.Query, dbname string) bool {
   280  	if query.Database != "" && query.Database != dbname {
   281  		return false
   282  	}
   283  	return true
   284  }
   285  
   286  func mustSendDDL(query mysql.Query, dbname string, filter *binlogdatapb.Filter) bool {
   287  	if query.Database != "" && query.Database != dbname {
   288  		return false
   289  	}
   290  	ast, err := sqlparser.Parse(query.SQL)
   291  	// If there was a parsing error, we send it through. Hopefully,
   292  	// recipient can handle it.
   293  	if err != nil {
   294  		return true
   295  	}
   296  	switch stmt := ast.(type) {
   297  	case sqlparser.DBDDLStatement:
   298  		return false
   299  	case sqlparser.DDLStatement:
   300  		if !stmt.GetTable().IsEmpty() {
   301  			return tableMatches(stmt.GetTable(), dbname, filter)
   302  		}
   303  		for _, table := range stmt.GetFromTables() {
   304  			if tableMatches(table, dbname, filter) {
   305  				return true
   306  			}
   307  		}
   308  		for _, table := range stmt.GetToTables() {
   309  			if tableMatches(table, dbname, filter) {
   310  				return true
   311  			}
   312  		}
   313  		return false
   314  	}
   315  	return true
   316  }
   317  
   318  func ruleMatches(tableName string, filter *binlogdatapb.Filter) bool {
   319  	for _, rule := range filter.Rules {
   320  		switch {
   321  		case strings.HasPrefix(rule.Match, "/"):
   322  			expr := strings.Trim(rule.Match, "/")
   323  			result, err := regexp.MatchString(expr, tableName)
   324  			if err != nil {
   325  				return false
   326  			}
   327  			if !result {
   328  				continue
   329  			}
   330  			return true
   331  		case tableName == rule.Match:
   332  			return true
   333  		}
   334  	}
   335  	return false
   336  }
   337  
   338  // tableMatches is similar to buildPlan below and MatchTable in vreplication/table_plan_builder.go.
   339  func tableMatches(table sqlparser.TableName, dbname string, filter *binlogdatapb.Filter) bool {
   340  	if !table.Qualifier.IsEmpty() && table.Qualifier.String() != dbname {
   341  		return false
   342  	}
   343  	return ruleMatches(table.Name.String(), filter)
   344  }
   345  
   346  func buildPlan(ti *Table, vschema *localVSchema, filter *binlogdatapb.Filter) (*Plan, error) {
   347  	for _, rule := range filter.Rules {
   348  		switch {
   349  		case strings.HasPrefix(rule.Match, "/"):
   350  			expr := strings.Trim(rule.Match, "/")
   351  			result, err := regexp.MatchString(expr, ti.Name)
   352  			if err != nil {
   353  				return nil, err
   354  			}
   355  			if !result {
   356  				continue
   357  			}
   358  			return buildREPlan(ti, vschema, rule.Filter)
   359  		case rule.Match == ti.Name:
   360  			return buildTablePlan(ti, vschema, rule.Filter)
   361  		}
   362  	}
   363  	return nil, nil
   364  }
   365  
   366  // buildREPlan handles cases where Match has a regular expression.
   367  // If so, the Filter can be an empty string or a keyrange, like "-80".
   368  func buildREPlan(ti *Table, vschema *localVSchema, filter string) (*Plan, error) {
   369  	plan := &Plan{
   370  		Table: ti,
   371  	}
   372  	plan.ColExprs = make([]ColExpr, len(ti.Fields))
   373  	for i, col := range ti.Fields {
   374  		plan.ColExprs[i].ColNum = i
   375  		plan.ColExprs[i].Field = col
   376  	}
   377  	if filter == "" {
   378  		return plan, nil
   379  	}
   380  
   381  	// We need to additionally set VindexColumn, Vindex and KeyRange
   382  	// based on the Primary Vindex of the table.
   383  	cv, err := vschema.FindColVindex(ti.Name)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  	whereFilter := Filter{
   388  		Opcode: VindexMatch,
   389  		Vindex: cv.Vindex,
   390  	}
   391  	whereFilter.VindexColumns, err = buildVindexColumns(plan.Table, cv.Columns)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  
   396  	// Parse keyrange.
   397  	keyranges, err := key.ParseShardingSpec(filter)
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  	if len(keyranges) != 1 {
   402  		return nil, fmt.Errorf("error parsing keyrange: %v", filter)
   403  	}
   404  	whereFilter.KeyRange = keyranges[0]
   405  	plan.Filters = append(plan.Filters, whereFilter)
   406  	return plan, nil
   407  }
   408  
   409  // BuildTablePlan handles cases where a specific table name is specified.
   410  // The filter must be a select statement.
   411  func buildTablePlan(ti *Table, vschema *localVSchema, query string) (*Plan, error) {
   412  	sel, fromTable, err := analyzeSelect(query)
   413  	if err != nil {
   414  		log.Errorf("%s", err.Error())
   415  		return nil, err
   416  	}
   417  	if fromTable.String() != ti.Name {
   418  		log.Errorf("unsupported: select expression table %v does not match the table entry name %s", sqlparser.String(fromTable), ti.Name)
   419  		return nil, fmt.Errorf("unsupported: select expression table %v does not match the table entry name %s", sqlparser.String(fromTable), ti.Name)
   420  	}
   421  
   422  	plan := &Plan{
   423  		Table: ti,
   424  	}
   425  	if err := plan.analyzeWhere(vschema, sel.Where); err != nil {
   426  		log.Errorf("%s", err.Error())
   427  		return nil, err
   428  	}
   429  	if err := plan.analyzeExprs(vschema, sel.SelectExprs); err != nil {
   430  		log.Errorf("%s", err.Error())
   431  		return nil, err
   432  	}
   433  
   434  	if sel.Where == nil {
   435  		return plan, nil
   436  	}
   437  
   438  	return plan, nil
   439  }
   440  
   441  func analyzeSelect(query string) (sel *sqlparser.Select, fromTable sqlparser.IdentifierCS, err error) {
   442  	statement, err := sqlparser.Parse(query)
   443  	if err != nil {
   444  		return nil, fromTable, err
   445  	}
   446  	sel, ok := statement.(*sqlparser.Select)
   447  	if !ok {
   448  		return nil, fromTable, fmt.Errorf("unsupported: %v", sqlparser.String(statement))
   449  	}
   450  	if len(sel.From) > 1 {
   451  		return nil, fromTable, fmt.Errorf("unsupported: %v", sqlparser.String(sel))
   452  	}
   453  	node, ok := sel.From[0].(*sqlparser.AliasedTableExpr)
   454  	if !ok {
   455  		return nil, fromTable, fmt.Errorf("unsupported: %v", sqlparser.String(sel))
   456  	}
   457  	fromTable = sqlparser.GetTableName(node.Expr)
   458  	if fromTable.IsEmpty() {
   459  		return nil, fromTable, fmt.Errorf("unsupported: %v", sqlparser.String(sel))
   460  	}
   461  	return sel, fromTable, nil
   462  }
   463  
   464  // isConvertColumnUsingUTF8 returns 'true' when given column needs to be converted as UTF8
   465  // while read from source table
   466  func (plan *Plan) isConvertColumnUsingUTF8(columnName string) bool {
   467  	if plan.convertUsingUTF8Columns == nil {
   468  		return false
   469  	}
   470  	return plan.convertUsingUTF8Columns[columnName]
   471  }
   472  
   473  // setConvertColumnUsingUTF8 marks given column as needs to be converted as UTF8
   474  // while read from source table
   475  func (plan *Plan) setConvertColumnUsingUTF8(columnName string) {
   476  	if plan.convertUsingUTF8Columns == nil {
   477  		plan.convertUsingUTF8Columns = map[string]bool{}
   478  	}
   479  	plan.convertUsingUTF8Columns[columnName] = true
   480  }
   481  
   482  // setColumnFuncExpr sets the function expression for the column, which
   483  // can then be used when building the streamer's query.
   484  func (plan *Plan) setColumnFuncExpr(columnName string, funcExpr *sqlparser.FuncExpr) {
   485  	if plan.columnFuncExprs == nil {
   486  		plan.columnFuncExprs = map[string]*sqlparser.FuncExpr{}
   487  	}
   488  	plan.columnFuncExprs[columnName] = funcExpr
   489  }
   490  
   491  // getColumnFuncExpr returns a function expression if the column needs
   492  // one when building the streamer's query.
   493  func (plan *Plan) getColumnFuncExpr(columnName string) *sqlparser.FuncExpr {
   494  	if plan.columnFuncExprs == nil {
   495  		return nil
   496  	}
   497  	if val, ok := plan.columnFuncExprs[columnName]; ok {
   498  		return val
   499  	}
   500  	return nil
   501  }
   502  
   503  func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) error {
   504  	if where == nil {
   505  		return nil
   506  	}
   507  	exprs := splitAndExpression(nil, where.Expr)
   508  	for _, expr := range exprs {
   509  		switch expr := expr.(type) {
   510  		case *sqlparser.ComparisonExpr:
   511  			opcode, err := getOpcode(expr)
   512  			if err != nil {
   513  				return err
   514  			}
   515  			qualifiedName, ok := expr.Left.(*sqlparser.ColName)
   516  			if !ok {
   517  				return fmt.Errorf("unexpected: %v", sqlparser.String(expr))
   518  			}
   519  			if !qualifiedName.Qualifier.IsEmpty() {
   520  				return fmt.Errorf("unsupported qualifier for column: %v", sqlparser.String(qualifiedName))
   521  			}
   522  			colnum, err := findColumn(plan.Table, qualifiedName.Name)
   523  			if err != nil {
   524  				return err
   525  			}
   526  			val, ok := expr.Right.(*sqlparser.Literal)
   527  			if !ok {
   528  				return fmt.Errorf("unexpected: %v", sqlparser.String(expr))
   529  			}
   530  			//StrVal is varbinary, we do not support varchar since we would have to implement all collation types
   531  			if val.Type != sqlparser.IntVal && val.Type != sqlparser.StrVal {
   532  				return fmt.Errorf("unexpected: %v", sqlparser.String(expr))
   533  			}
   534  			pv, err := evalengine.Translate(val, semantics.EmptySemTable())
   535  			if err != nil {
   536  				return err
   537  			}
   538  			env := evalengine.EmptyExpressionEnv()
   539  			resolved, err := env.Evaluate(pv)
   540  			if err != nil {
   541  				return err
   542  			}
   543  			plan.Filters = append(plan.Filters, Filter{
   544  				Opcode: opcode,
   545  				ColNum: colnum,
   546  				Value:  resolved.Value(),
   547  			})
   548  		case *sqlparser.FuncExpr:
   549  			if !expr.Name.EqualString("in_keyrange") {
   550  				return fmt.Errorf("unsupported constraint: %v", sqlparser.String(expr))
   551  			}
   552  			if err := plan.analyzeInKeyRange(vschema, expr.Exprs); err != nil {
   553  				return err
   554  			}
   555  		default:
   556  			return fmt.Errorf("unsupported constraint: %v", sqlparser.String(expr))
   557  		}
   558  	}
   559  	return nil
   560  }
   561  
   562  // splitAndExpression breaks up the Expr into AND-separated conditions
   563  // and appends them to filters, which can be shuffled and recombined
   564  // as needed.
   565  func splitAndExpression(filters []sqlparser.Expr, node sqlparser.Expr) []sqlparser.Expr {
   566  	if node == nil {
   567  		return filters
   568  	}
   569  	switch node := node.(type) {
   570  	case *sqlparser.AndExpr:
   571  		filters = splitAndExpression(filters, node.Left)
   572  		return splitAndExpression(filters, node.Right)
   573  	}
   574  	return append(filters, node)
   575  }
   576  
   577  func (plan *Plan) analyzeExprs(vschema *localVSchema, selExprs sqlparser.SelectExprs) error {
   578  	if _, ok := selExprs[0].(*sqlparser.StarExpr); !ok {
   579  		for _, expr := range selExprs {
   580  			cExpr, err := plan.analyzeExpr(vschema, expr)
   581  			if err != nil {
   582  				return err
   583  			}
   584  			plan.ColExprs = append(plan.ColExprs, cExpr)
   585  		}
   586  	} else {
   587  		if len(selExprs) != 1 {
   588  			return fmt.Errorf("unsupported: %v", sqlparser.String(selExprs))
   589  		}
   590  		plan.ColExprs = make([]ColExpr, len(plan.Table.Fields))
   591  		for i, col := range plan.Table.Fields {
   592  			plan.ColExprs[i].ColNum = i
   593  			plan.ColExprs[i].Field = col
   594  		}
   595  	}
   596  	return nil
   597  }
   598  
   599  func (plan *Plan) analyzeExpr(vschema *localVSchema, selExpr sqlparser.SelectExpr) (cExpr ColExpr, err error) {
   600  	aliased, ok := selExpr.(*sqlparser.AliasedExpr)
   601  	if !ok {
   602  		return ColExpr{}, fmt.Errorf("unsupported: %v", sqlparser.String(selExpr))
   603  	}
   604  	switch inner := aliased.Expr.(type) {
   605  	case *sqlparser.ColName:
   606  		if !inner.Qualifier.IsEmpty() {
   607  			return ColExpr{}, fmt.Errorf("unsupported qualifier for column: %v", sqlparser.String(inner))
   608  		}
   609  		colnum, err := findColumn(plan.Table, inner.Name)
   610  		if err != nil {
   611  			return ColExpr{}, err
   612  		}
   613  		return ColExpr{
   614  			ColNum: colnum,
   615  			Field:  plan.Table.Fields[colnum],
   616  		}, nil
   617  	case sqlparser.AggrFunc:
   618  		if strings.ToLower(inner.AggrName()) != "keyspace_id" {
   619  			return ColExpr{}, fmt.Errorf("unsupported function: %v", sqlparser.String(inner))
   620  		}
   621  		if len(inner.GetArgs()) != 0 {
   622  			return ColExpr{}, fmt.Errorf("unexpected: %v", sqlparser.String(inner))
   623  		}
   624  		cv, err := vschema.FindColVindex(plan.Table.Name)
   625  		if err != nil {
   626  			return ColExpr{}, err
   627  		}
   628  		vindexColumns, err := buildVindexColumns(plan.Table, cv.Columns)
   629  		if err != nil {
   630  			return ColExpr{}, err
   631  		}
   632  		return ColExpr{
   633  			Field: &querypb.Field{
   634  				Name: "keyspace_id",
   635  				Type: sqltypes.VarBinary,
   636  			},
   637  			Vindex:        cv.Vindex,
   638  			VindexColumns: vindexColumns,
   639  		}, nil
   640  	case *sqlparser.FuncExpr:
   641  		switch inner.Name.Lowered() {
   642  		case "keyspace_id":
   643  			// This function is used internally to route queries and records properly
   644  			// in sharded keyspaces using vindexes.
   645  			if len(inner.Exprs) != 0 {
   646  				return ColExpr{}, fmt.Errorf("unexpected: %v", sqlparser.String(inner))
   647  			}
   648  			cv, err := vschema.FindColVindex(plan.Table.Name)
   649  			if err != nil {
   650  				return ColExpr{}, err
   651  			}
   652  			vindexColumns, err := buildVindexColumns(plan.Table, cv.Columns)
   653  			if err != nil {
   654  				return ColExpr{}, err
   655  			}
   656  			return ColExpr{
   657  				Field: &querypb.Field{
   658  					Name: "keyspace_id",
   659  					Type: sqltypes.VarBinary,
   660  				},
   661  				Vindex:        cv.Vindex,
   662  				VindexColumns: vindexColumns,
   663  			}, nil
   664  		case "convert_tz":
   665  			// This function is used when transforming datetime
   666  			// values between the source and target.
   667  			colnum, err := findColumn(plan.Table, aliased.As)
   668  			if err != nil {
   669  				return ColExpr{}, err
   670  			}
   671  			field := plan.Table.Fields[colnum]
   672  			plan.setColumnFuncExpr(field.Name, inner)
   673  			return ColExpr{
   674  				ColNum: colnum,
   675  				Field:  field,
   676  			}, nil
   677  		default:
   678  			return ColExpr{}, fmt.Errorf("unsupported function: %v", sqlparser.String(inner))
   679  		}
   680  	case *sqlparser.Literal:
   681  		//allow only intval 1
   682  		if inner.Type != sqlparser.IntVal {
   683  			return ColExpr{}, fmt.Errorf("only integer literals are supported")
   684  		}
   685  		num, err := strconv.ParseInt(string(inner.Val), 0, 64)
   686  		if err != nil {
   687  			return ColExpr{}, err
   688  		}
   689  		if num != 1 {
   690  			return ColExpr{}, fmt.Errorf("only the integer literal 1 is supported")
   691  		}
   692  		return ColExpr{
   693  			Field: &querypb.Field{
   694  				Name: "1",
   695  				Type: querypb.Type_INT64,
   696  			},
   697  			ColNum:     -1,
   698  			FixedValue: sqltypes.NewInt64(num),
   699  		}, nil
   700  	case *sqlparser.ConvertUsingExpr:
   701  		colnum, err := findColumn(plan.Table, aliased.As)
   702  		if err != nil {
   703  			return ColExpr{}, err
   704  		}
   705  		field := plan.Table.Fields[colnum]
   706  		plan.setConvertColumnUsingUTF8(field.Name)
   707  		return ColExpr{
   708  			ColNum: colnum,
   709  			Field:  field,
   710  		}, nil
   711  	default:
   712  		log.Infof("Unsupported expression: %v", inner)
   713  		return ColExpr{}, fmt.Errorf("unsupported: %v", sqlparser.String(aliased.Expr))
   714  	}
   715  }
   716  
   717  // analyzeInKeyRange allows the following constructs: "in_keyrange('-80')",
   718  // "in_keyrange(col, 'hash', '-80')", "in_keyrange(col, 'local_vindex', '-80')", or
   719  // "in_keyrange(col, 'ks.external_vindex', '-80')".
   720  func (plan *Plan) analyzeInKeyRange(vschema *localVSchema, exprs sqlparser.SelectExprs) error {
   721  	var colnames []sqlparser.IdentifierCI
   722  	var krExpr sqlparser.SelectExpr
   723  	whereFilter := Filter{
   724  		Opcode: VindexMatch,
   725  	}
   726  	switch {
   727  	case len(exprs) == 1:
   728  		cv, err := vschema.FindColVindex(plan.Table.Name)
   729  		if err != nil {
   730  			return err
   731  		}
   732  		colnames = cv.Columns
   733  		whereFilter.Vindex = cv.Vindex
   734  		krExpr = exprs[0]
   735  	case len(exprs) >= 3:
   736  		for _, expr := range exprs[:len(exprs)-2] {
   737  			aexpr, ok := expr.(*sqlparser.AliasedExpr)
   738  			if !ok {
   739  				return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected: %T %s", expr, sqlparser.String(expr))
   740  			}
   741  			qualifiedName, ok := aexpr.Expr.(*sqlparser.ColName)
   742  			if !ok {
   743  				return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected: %T %s", aexpr.Expr, sqlparser.String(aexpr.Expr))
   744  			}
   745  			if !qualifiedName.Qualifier.IsEmpty() {
   746  				return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported qualifier for column: %v", sqlparser.String(qualifiedName))
   747  			}
   748  			colnames = append(colnames, qualifiedName.Name)
   749  		}
   750  
   751  		vtype, err := selString(exprs[len(exprs)-2])
   752  		if err != nil {
   753  			return err
   754  		}
   755  		whereFilter.Vindex, err = vschema.FindOrCreateVindex(vtype)
   756  		if err != nil {
   757  			return err
   758  		}
   759  		if !whereFilter.Vindex.IsUnique() {
   760  			return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex must be Unique to be used for VReplication: %s", vtype)
   761  		}
   762  
   763  		krExpr = exprs[len(exprs)-1]
   764  	default:
   765  		return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected in_keyrange parameters: %v", sqlparser.String(exprs))
   766  	}
   767  	var err error
   768  	whereFilter.VindexColumns, err = buildVindexColumns(plan.Table, colnames)
   769  	if err != nil {
   770  		return err
   771  	}
   772  	kr, err := selString(krExpr)
   773  	if err != nil {
   774  		return err
   775  	}
   776  	keyranges, err := key.ParseShardingSpec(kr)
   777  	if err != nil {
   778  		return err
   779  	}
   780  	if len(keyranges) != 1 {
   781  		return fmt.Errorf("unexpected in_keyrange parameter: %v", sqlparser.String(krExpr))
   782  	}
   783  	whereFilter.KeyRange = keyranges[0]
   784  	plan.Filters = append(plan.Filters, whereFilter)
   785  	return nil
   786  }
   787  
   788  func selString(expr sqlparser.SelectExpr) (string, error) {
   789  	aexpr, ok := expr.(*sqlparser.AliasedExpr)
   790  	if !ok {
   791  		return "", fmt.Errorf("unsupported: %v", sqlparser.String(expr))
   792  	}
   793  	val, ok := aexpr.Expr.(*sqlparser.Literal)
   794  	if !ok {
   795  		return "", fmt.Errorf("unsupported: %v", sqlparser.String(expr))
   796  	}
   797  	return string(val.Val), nil
   798  }
   799  
   800  // buildVindexColumns builds the list of column numbers of the table
   801  // that will be the input to the vindex function.
   802  func buildVindexColumns(ti *Table, colnames []sqlparser.IdentifierCI) ([]int, error) {
   803  	vindexColumns := make([]int, 0, len(colnames))
   804  	for _, colname := range colnames {
   805  		colnum, err := findColumn(ti, colname)
   806  		if err != nil {
   807  			return nil, err
   808  		}
   809  		vindexColumns = append(vindexColumns, colnum)
   810  	}
   811  	return vindexColumns, nil
   812  }
   813  
   814  func findColumn(ti *Table, name sqlparser.IdentifierCI) (int, error) {
   815  	for i, col := range ti.Fields {
   816  		if name.EqualString(col.Name) {
   817  			return i, nil
   818  		}
   819  	}
   820  	return 0, fmt.Errorf("column %s not found in table %s", sqlparser.String(name), ti.Name)
   821  }