vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/vindex_func.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 planbuilder
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    23  
    24  	"vitess.io/vitess/go/vt/vtgate/semantics"
    25  
    26  	"vitess.io/vitess/go/vt/vterrors"
    27  
    28  	"vitess.io/vitess/go/vt/sqlparser"
    29  	"vitess.io/vitess/go/vt/vtgate/engine"
    30  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    31  
    32  	querypb "vitess.io/vitess/go/vt/proto/query"
    33  )
    34  
    35  var _ logicalPlan = (*vindexFunc)(nil)
    36  
    37  // vindexFunc is used to build a VindexFunc primitive.
    38  type vindexFunc struct {
    39  	order int
    40  
    41  	// the tableID field is only used by the gen4 planner
    42  	tableID semantics.TableSet
    43  
    44  	// resultColumns represent the columns returned by this route.
    45  	resultColumns []*resultColumn
    46  
    47  	// eVindexFunc is the primitive being built.
    48  	eVindexFunc *engine.VindexFunc
    49  }
    50  
    51  var colnames = []string{
    52  	"id",
    53  	"keyspace_id",
    54  	"range_start",
    55  	"range_end",
    56  	"hex_keyspace_id",
    57  	"shard",
    58  }
    59  
    60  func newVindexFunc(alias sqlparser.TableName, vindex vindexes.SingleColumn) (*vindexFunc, *symtab) {
    61  	vf := &vindexFunc{
    62  		order: 1,
    63  		eVindexFunc: &engine.VindexFunc{
    64  			Vindex: vindex,
    65  		},
    66  	}
    67  
    68  	// Create a 'table' that represents the vindex.
    69  	t := &table{
    70  		alias:  alias,
    71  		origin: vf,
    72  	}
    73  
    74  	for _, colName := range colnames {
    75  		t.addColumn(sqlparser.NewIdentifierCI(colName), &column{origin: vf})
    76  	}
    77  	t.isAuthoritative = true
    78  
    79  	st := newSymtab()
    80  	// AddTable will not fail because symtab is empty.
    81  	_ = st.AddTable(t)
    82  	return vf, st
    83  }
    84  
    85  // Order implements the logicalPlan interface
    86  func (vf *vindexFunc) Order() int {
    87  	return vf.order
    88  }
    89  
    90  // Reorder implements the logicalPlan interface
    91  func (vf *vindexFunc) Reorder(order int) {
    92  	vf.order = order + 1
    93  }
    94  
    95  // Primitive implements the logicalPlan interface
    96  func (vf *vindexFunc) Primitive() engine.Primitive {
    97  	return vf.eVindexFunc
    98  }
    99  
   100  // ResultColumns implements the logicalPlan interface
   101  func (vf *vindexFunc) ResultColumns() []*resultColumn {
   102  	return vf.resultColumns
   103  }
   104  
   105  // Wireup implements the logicalPlan interface
   106  func (vf *vindexFunc) Wireup(logicalPlan, *jointab) error {
   107  	return nil
   108  }
   109  
   110  // WireupGen4 implements the logicalPlan interface
   111  func (vf *vindexFunc) WireupGen4(*plancontext.PlanningContext) error {
   112  	return nil
   113  }
   114  
   115  // SupplyVar implements the logicalPlan interface
   116  func (vf *vindexFunc) SupplyVar(from, to int, col *sqlparser.ColName, varname string) {
   117  	// vindexFunc is an atomic primitive. So, SupplyVar cannot be
   118  	// called on it.
   119  	panic("BUG: vindexFunc is an atomic node.")
   120  }
   121  
   122  // SupplyCol implements the logicalPlan interface
   123  func (vf *vindexFunc) SupplyCol(col *sqlparser.ColName) (rc *resultColumn, colNumber int) {
   124  	c := col.Metadata.(*column)
   125  	for i, rc := range vf.resultColumns {
   126  		if rc.column == c {
   127  			return rc, i
   128  		}
   129  	}
   130  
   131  	vf.resultColumns = append(vf.resultColumns, &resultColumn{column: c})
   132  	vf.eVindexFunc.Fields = append(vf.eVindexFunc.Fields, &querypb.Field{
   133  		Name: col.Name.String(),
   134  		Type: querypb.Type_VARBINARY,
   135  	})
   136  
   137  	// columns that reference vindexFunc will have their colNumber set.
   138  	// Let's use it here.
   139  	vf.eVindexFunc.Cols = append(vf.eVindexFunc.Cols, c.colNumber)
   140  	return rc, len(vf.resultColumns) - 1
   141  }
   142  
   143  // SupplyProjection pushes the given aliased expression into the fields and cols slices of the
   144  // vindexFunc engine primitive. The method returns the offset of the new expression in the columns
   145  // list.
   146  func (vf *vindexFunc) SupplyProjection(expr *sqlparser.AliasedExpr, reuse bool) (int, error) {
   147  	colName, isColName := expr.Expr.(*sqlparser.ColName)
   148  	if !isColName {
   149  		return 0, vterrors.VT12001("expression on results of a vindex function")
   150  	}
   151  
   152  	enum := vindexColumnToIndex(colName)
   153  	if enum == -1 {
   154  		return 0, vterrors.VT03016(colName.Name.String())
   155  	}
   156  
   157  	if reuse {
   158  		for i, col := range vf.eVindexFunc.Cols {
   159  			if col == enum {
   160  				return i, nil
   161  			}
   162  		}
   163  	}
   164  
   165  	vf.eVindexFunc.Fields = append(vf.eVindexFunc.Fields, &querypb.Field{
   166  		Name: expr.ColumnName(),
   167  		Type: querypb.Type_VARBINARY,
   168  	})
   169  	vf.eVindexFunc.Cols = append(vf.eVindexFunc.Cols, enum)
   170  	return len(vf.eVindexFunc.Cols) - 1, nil
   171  }
   172  
   173  // UnsupportedSupplyWeightString represents the error where the supplying a weight string is not supported
   174  type UnsupportedSupplyWeightString struct {
   175  	Type string
   176  }
   177  
   178  // Error function implements the error interface
   179  func (err UnsupportedSupplyWeightString) Error() string {
   180  	return fmt.Sprintf("cannot do collation on %s", err.Type)
   181  }
   182  
   183  // SupplyWeightString implements the logicalPlan interface
   184  func (vf *vindexFunc) SupplyWeightString(colNumber int, alsoAddToGroupBy bool) (weightcolNumber int, err error) {
   185  	return 0, UnsupportedSupplyWeightString{Type: "vindex function"}
   186  }
   187  
   188  // Rewrite implements the logicalPlan interface
   189  func (vf *vindexFunc) Rewrite(inputs ...logicalPlan) error {
   190  	if len(inputs) != 0 {
   191  		return vterrors.VT13001("vindexFunc: wrong number of inputs")
   192  	}
   193  	return nil
   194  }
   195  
   196  // ContainsTables implements the logicalPlan interface
   197  func (vf *vindexFunc) ContainsTables() semantics.TableSet {
   198  	return vf.tableID
   199  }
   200  
   201  // Inputs implements the logicalPlan interface
   202  func (vf *vindexFunc) Inputs() []logicalPlan {
   203  	return []logicalPlan{}
   204  }
   205  
   206  func vindexColumnToIndex(column *sqlparser.ColName) int {
   207  	switch column.Name.String() {
   208  	case "id":
   209  		return 0
   210  	case "keyspace_id":
   211  		return 1
   212  	case "range_start":
   213  		return 2
   214  	case "range_end":
   215  		return 3
   216  	case "hex_keyspace_id":
   217  		return 4
   218  	case "shard":
   219  		return 5
   220  	default:
   221  		return -1
   222  	}
   223  }
   224  
   225  // OutputColumns implements the logicalPlan interface
   226  func (vf *vindexFunc) OutputColumns() []sqlparser.SelectExpr {
   227  	exprs := make([]sqlparser.SelectExpr, 0, len(colnames))
   228  	for _, field := range vf.eVindexFunc.Fields {
   229  		exprs = append(exprs, &sqlparser.AliasedExpr{Expr: sqlparser.NewColName(field.Name)})
   230  	}
   231  	return exprs
   232  }