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 }