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 }