vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/insert.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 engine 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "strconv" 24 "strings" 25 "sync" 26 "time" 27 28 "vitess.io/vitess/go/vt/sqlparser" 29 30 "vitess.io/vitess/go/vt/vtgate/evalengine" 31 32 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 33 34 "vitess.io/vitess/go/sqltypes" 35 "vitess.io/vitess/go/vt/key" 36 "vitess.io/vitess/go/vt/srvtopo" 37 "vitess.io/vitess/go/vt/vterrors" 38 "vitess.io/vitess/go/vt/vtgate/vindexes" 39 40 querypb "vitess.io/vitess/go/vt/proto/query" 41 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 42 ) 43 44 var _ Primitive = (*Insert)(nil) 45 46 type ( 47 // Insert represents the instructions to perform an insert operation. 48 Insert struct { 49 // Opcode is the execution opcode. 50 Opcode InsertOpcode 51 52 // Ignore is for INSERT IGNORE and INSERT...ON DUPLICATE KEY constructs 53 // for sharded cases. 54 Ignore bool 55 56 // Keyspace specifies the keyspace to send the query to. 57 Keyspace *vindexes.Keyspace 58 59 // Query specifies the query to be executed. 60 // For InsertSharded plans, this value is unused, 61 // and Prefix, Mid and Suffix are used instead. 62 Query string 63 64 // VindexValues specifies values for all the vindex columns. 65 // This is a three-dimensional data structure: 66 // Insert.Values[i] represents the values to be inserted for the i'th colvindex (i < len(Insert.Table.ColumnVindexes)) 67 // Insert.Values[i].Values[j] represents values for the j'th column of the given colVindex (j < len(colVindex[i].Columns) 68 // Insert.Values[i].Values[j].Values[k] represents the value pulled from row k for that column: (k < len(ins.rows)) 69 VindexValues [][][]evalengine.Expr 70 71 // ColVindexes are the vindexes that will use the VindexValues 72 ColVindexes []*vindexes.ColumnVindex 73 74 // Table specifies the table for the insert. 75 Table *vindexes.Table 76 77 // Generate is only set for inserts where a sequence must be generated. 78 Generate *Generate 79 80 // Prefix, Mid and Suffix are for sharded insert plans. 81 Prefix string 82 Mid []string 83 Suffix string 84 85 // Option to override the standard behavior and allow a multi-shard insert 86 // to use single round trip autocommit. 87 // 88 // This is a clear violation of the SQL semantics since it means the statement 89 // is not atomic in the presence of PK conflicts on one shard and not another. 90 // However some application use cases would prefer that the statement partially 91 // succeed in order to get the performance benefits of autocommit. 92 MultiShardAutocommit bool 93 94 // QueryTimeout contains the optional timeout (in milliseconds) to apply to this query 95 QueryTimeout int 96 97 // VindexValueOffset stores the offset for each column in the ColumnVindex 98 // that will appear in the result set of the select query. 99 VindexValueOffset [][]int 100 101 // Input is a select query plan to retrieve results for inserting data. 102 Input Primitive `json:",omitempty"` 103 104 // ForceNonStreaming is true when the insert table and select table are same. 105 // This will avoid locking by the select table. 106 ForceNonStreaming bool 107 108 // Insert needs tx handling 109 txNeeded 110 } 111 112 ksID = []byte 113 ) 114 115 func (ins *Insert) Inputs() []Primitive { 116 if ins.Input == nil { 117 return nil 118 } 119 return []Primitive{ins.Input} 120 } 121 122 // NewQueryInsert creates an Insert with a query string. 123 func NewQueryInsert(opcode InsertOpcode, keyspace *vindexes.Keyspace, query string) *Insert { 124 return &Insert{ 125 Opcode: opcode, 126 Keyspace: keyspace, 127 Query: query, 128 } 129 } 130 131 // NewSimpleInsert creates an Insert for a Table. 132 func NewSimpleInsert(opcode InsertOpcode, table *vindexes.Table, keyspace *vindexes.Keyspace) *Insert { 133 return &Insert{ 134 Opcode: opcode, 135 Table: table, 136 Keyspace: keyspace, 137 } 138 } 139 140 // NewInsert creates a new Insert. 141 func NewInsert( 142 opcode InsertOpcode, 143 ignore bool, 144 keyspace *vindexes.Keyspace, 145 vindexValues [][][]evalengine.Expr, 146 table *vindexes.Table, 147 prefix string, 148 mid []string, 149 suffix string, 150 ) *Insert { 151 return &Insert{ 152 Opcode: opcode, 153 Ignore: ignore, 154 Keyspace: keyspace, 155 VindexValues: vindexValues, 156 Table: table, 157 Prefix: prefix, 158 Mid: mid, 159 Suffix: suffix, 160 } 161 } 162 163 // Generate represents the instruction to generate 164 // a value from a sequence. 165 type Generate struct { 166 Keyspace *vindexes.Keyspace 167 Query string 168 // Values are the supplied values for the column, which 169 // will be stored as a list within the expression. New 170 // values will be generated based on how many were not 171 // supplied (NULL). 172 Values evalengine.Expr 173 // Insert using Select, offset for auto increment column 174 Offset int 175 } 176 177 // InsertOpcode is a number representing the opcode 178 // for the Insert primitive. 179 type InsertOpcode int 180 181 const ( 182 // InsertUnsharded is for routing an insert statement 183 // to an unsharded keyspace. 184 InsertUnsharded = InsertOpcode(iota) 185 // InsertSharded is for routing an insert statement 186 // to individual shards. Requires: A list of Values, one 187 // for each ColVindex. If the table has an Autoinc column, 188 // A Generate subplan must be created. 189 InsertSharded 190 // InsertSelect is for routing an insert statement 191 // based on rows returned from the select statement. 192 InsertSelect 193 ) 194 195 var insName = map[InsertOpcode]string{ 196 InsertUnsharded: "InsertUnsharded", 197 InsertSharded: "InsertSharded", 198 InsertSelect: "InsertSelect", 199 } 200 201 // String returns the opcode 202 func (code InsertOpcode) String() string { 203 return strings.ReplaceAll(insName[code], "Insert", "") 204 } 205 206 // MarshalJSON serializes the InsertOpcode as a JSON string. 207 // It's used for testing and diagnostics. 208 func (code InsertOpcode) MarshalJSON() ([]byte, error) { 209 return json.Marshal(insName[code]) 210 } 211 212 // RouteType returns a description of the query routing type used by the primitive 213 func (ins *Insert) RouteType() string { 214 return insName[ins.Opcode] 215 } 216 217 // GetKeyspaceName specifies the Keyspace that this primitive routes to. 218 func (ins *Insert) GetKeyspaceName() string { 219 return ins.Keyspace.Name 220 } 221 222 // GetTableName specifies the table that this primitive routes to. 223 func (ins *Insert) GetTableName() string { 224 if ins.Table != nil { 225 return ins.Table.Name.String() 226 } 227 return "" 228 } 229 230 // TryExecute performs a non-streaming exec. 231 func (ins *Insert) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { 232 ctx, cancelFunc := addQueryTimeout(ctx, vcursor, ins.QueryTimeout) 233 defer cancelFunc() 234 235 switch ins.Opcode { 236 case InsertUnsharded: 237 return ins.execInsertUnsharded(ctx, vcursor, bindVars) 238 case InsertSharded: 239 return ins.execInsertSharded(ctx, vcursor, bindVars) 240 case InsertSelect: 241 return ins.execInsertFromSelect(ctx, vcursor, bindVars) 242 default: 243 // Unreachable. 244 return nil, fmt.Errorf("unsupported query route: %v", ins) 245 } 246 } 247 248 // TryStreamExecute performs a streaming exec. 249 func (ins *Insert) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { 250 if ins.Input == nil || ins.ForceNonStreaming { 251 res, err := ins.TryExecute(ctx, vcursor, bindVars, wantfields) 252 if err != nil { 253 return err 254 } 255 return callback(res) 256 } 257 if ins.QueryTimeout != 0 { 258 var cancel context.CancelFunc 259 ctx, cancel = context.WithTimeout(ctx, time.Duration(ins.QueryTimeout)*time.Millisecond) 260 defer cancel() 261 } 262 263 unsharded := ins.Opcode == InsertUnsharded 264 var mu sync.Mutex 265 output := &sqltypes.Result{} 266 267 err := vcursor.StreamExecutePrimitiveStandalone(ctx, ins.Input, bindVars, false, func(result *sqltypes.Result) error { 268 if len(result.Rows) == 0 { 269 return nil 270 } 271 272 // should process only one chunk at a time. 273 // as parallel chunk insert will try to use the same transaction in the vttablet 274 // this will cause transaction in use error. 275 mu.Lock() 276 defer mu.Unlock() 277 278 var insertID int64 279 var qr *sqltypes.Result 280 var err error 281 if unsharded { 282 insertID, qr, err = ins.insertIntoUnshardedTable(ctx, vcursor, bindVars, result) 283 } else { 284 insertID, qr, err = ins.insertIntoShardedTable(ctx, vcursor, bindVars, result) 285 } 286 if err != nil { 287 return err 288 } 289 290 output.RowsAffected += qr.RowsAffected 291 // InsertID needs to be updated to the least insertID value in sqltypes.Result 292 if output.InsertID == 0 || output.InsertID > uint64(insertID) { 293 output.InsertID = uint64(insertID) 294 } 295 return nil 296 }) 297 if err != nil { 298 return err 299 } 300 return callback(output) 301 } 302 303 func (ins *Insert) insertIntoShardedTable(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, result *sqltypes.Result) (int64, *sqltypes.Result, error) { 304 insertID, err := ins.processGenerateFromRows(ctx, vcursor, result.Rows) 305 if err != nil { 306 return 0, nil, err 307 } 308 309 rss, queries, err := ins.getInsertSelectQueries(ctx, vcursor, bindVars, result.Rows) 310 if err != nil { 311 return 0, nil, err 312 } 313 314 qr, err := ins.executeInsertQueries(ctx, vcursor, rss, queries, insertID) 315 if err != nil { 316 return 0, nil, err 317 } 318 return insertID, qr, nil 319 } 320 321 // GetFields fetches the field info. 322 func (ins *Insert) GetFields(context.Context, VCursor, map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 323 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unreachable code for %q", ins.Query) 324 } 325 326 func (ins *Insert) execInsertUnsharded(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 327 query := ins.Query 328 if ins.Input != nil { 329 result, err := vcursor.ExecutePrimitive(ctx, ins.Input, bindVars, false) 330 if err != nil { 331 return nil, err 332 } 333 if len(result.Rows) == 0 { 334 return &sqltypes.Result{}, nil 335 } 336 query = ins.getInsertQueryForUnsharded(result, bindVars) 337 } 338 339 _, qr, err := ins.executeUnshardedTableQuery(ctx, vcursor, bindVars, query) 340 return qr, err 341 } 342 343 func (ins *Insert) getInsertQueryForUnsharded(result *sqltypes.Result, bindVars map[string]*querypb.BindVariable) string { 344 var mids sqlparser.Values 345 for r, inputRow := range result.Rows { 346 row := sqlparser.ValTuple{} 347 for c, value := range inputRow { 348 bvName := insertVarOffset(r, c) 349 bindVars[bvName] = sqltypes.ValueBindVariable(value) 350 row = append(row, sqlparser.NewArgument(bvName)) 351 } 352 mids = append(mids, row) 353 } 354 return ins.Prefix + sqlparser.String(mids) + ins.Suffix 355 } 356 357 func (ins *Insert) execInsertSharded(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 358 insertID, err := ins.processGenerateFromValues(ctx, vcursor, bindVars) 359 if err != nil { 360 return nil, err 361 } 362 rss, queries, err := ins.getInsertShardedRoute(ctx, vcursor, bindVars) 363 if err != nil { 364 return nil, err 365 } 366 367 return ins.executeInsertQueries(ctx, vcursor, rss, queries, insertID) 368 } 369 370 func (ins *Insert) executeInsertQueries( 371 ctx context.Context, 372 vcursor VCursor, 373 rss []*srvtopo.ResolvedShard, 374 queries []*querypb.BoundQuery, 375 insertID int64, 376 ) (*sqltypes.Result, error) { 377 autocommit := (len(rss) == 1 || ins.MultiShardAutocommit) && vcursor.AutocommitApproval() 378 err := allowOnlyPrimary(rss...) 379 if err != nil { 380 return nil, err 381 } 382 result, errs := vcursor.ExecuteMultiShard(ctx, ins, rss, queries, true /* rollbackOnError */, autocommit) 383 if errs != nil { 384 return nil, vterrors.Aggregate(errs) 385 } 386 387 if insertID != 0 { 388 result.InsertID = uint64(insertID) 389 } 390 return result, nil 391 } 392 393 func (ins *Insert) getInsertSelectQueries( 394 ctx context.Context, 395 vcursor VCursor, 396 bindVars map[string]*querypb.BindVariable, 397 rows []sqltypes.Row, 398 ) ([]*srvtopo.ResolvedShard, []*querypb.BoundQuery, error) { 399 colVindexes := ins.ColVindexes 400 if colVindexes == nil { 401 colVindexes = ins.Table.ColumnVindexes 402 } 403 404 if len(colVindexes) != len(ins.VindexValueOffset) { 405 return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex value offsets and vindex info do not match") 406 } 407 408 // Here we go over the incoming rows and extract values for the vindexes we need to update 409 shardingCols := make([][]sqltypes.Row, len(colVindexes)) 410 for _, inputRow := range rows { 411 for colIdx := range colVindexes { 412 offsets := ins.VindexValueOffset[colIdx] 413 row := make(sqltypes.Row, 0, len(offsets)) 414 for _, offset := range offsets { 415 if offset == -1 { // value not provided from select query 416 row = append(row, sqltypes.NULL) 417 continue 418 } 419 row = append(row, inputRow[offset]) 420 } 421 shardingCols[colIdx] = append(shardingCols[colIdx], row) 422 } 423 } 424 425 keyspaceIDs, err := ins.processPrimary(ctx, vcursor, shardingCols[0], colVindexes[0]) 426 if err != nil { 427 return nil, nil, err 428 } 429 430 for vIdx := 1; vIdx < len(colVindexes); vIdx++ { 431 colVindex := colVindexes[vIdx] 432 var err error 433 if colVindex.Owned { 434 err = ins.processOwned(ctx, vcursor, shardingCols[vIdx], colVindex, keyspaceIDs) 435 } else { 436 err = ins.processUnowned(ctx, vcursor, shardingCols[vIdx], colVindex, keyspaceIDs) 437 } 438 if err != nil { 439 return nil, nil, err 440 } 441 } 442 443 var indexes []*querypb.Value 444 var destinations []key.Destination 445 for i, ksid := range keyspaceIDs { 446 if ksid != nil { 447 indexes = append(indexes, &querypb.Value{ 448 Value: strconv.AppendInt(nil, int64(i), 10), 449 }) 450 destinations = append(destinations, key.DestinationKeyspaceID(ksid)) 451 } 452 } 453 if len(destinations) == 0 { 454 // In this case, all we have is nil KeyspaceIds, we don't do 455 // anything at all. 456 return nil, nil, nil 457 } 458 459 rss, indexesPerRss, err := vcursor.ResolveDestinations(ctx, ins.Keyspace.Name, indexes, destinations) 460 if err != nil { 461 return nil, nil, err 462 } 463 464 queries := make([]*querypb.BoundQuery, len(rss)) 465 for i := range rss { 466 bvs := sqltypes.CopyBindVariables(bindVars) // we don't want to create one huge bindvars for all values 467 var mids sqlparser.Values 468 for _, indexValue := range indexesPerRss[i] { 469 index, _ := strconv.ParseInt(string(indexValue.Value), 0, 64) 470 if keyspaceIDs[index] != nil { 471 row := sqlparser.ValTuple{} 472 for colOffset, value := range rows[index] { 473 bvName := insertVarOffset(int(index), colOffset) 474 bvs[bvName] = sqltypes.ValueBindVariable(value) 475 row = append(row, sqlparser.NewArgument(bvName)) 476 } 477 mids = append(mids, row) 478 } 479 } 480 rewritten := ins.Prefix + sqlparser.String(mids) + ins.Suffix 481 queries[i] = &querypb.BoundQuery{ 482 Sql: rewritten, 483 BindVariables: bvs, 484 } 485 } 486 487 return rss, queries, nil 488 } 489 490 func (ins *Insert) execInsertFromSelect(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 491 // run the SELECT query 492 if ins.Input == nil { 493 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "something went wrong planning INSERT SELECT") 494 } 495 496 result, err := vcursor.ExecutePrimitive(ctx, ins.Input, bindVars, false) 497 if err != nil { 498 return nil, err 499 } 500 if len(result.Rows) == 0 { 501 return &sqltypes.Result{}, nil 502 } 503 504 _, qr, err := ins.insertIntoShardedTable(ctx, vcursor, bindVars, result) 505 return qr, err 506 } 507 508 // shouldGenerate determines if a sequence value should be generated for a given value 509 func shouldGenerate(v sqltypes.Value) bool { 510 if v.IsNull() { 511 return true 512 } 513 514 // Unless the NO_AUTO_VALUE_ON_ZERO sql mode is active in mysql, it also 515 // treats 0 as a value that should generate a new sequence. 516 n, err := evalengine.ToUint64(v) 517 if err == nil && n == 0 { 518 return true 519 } 520 521 return false 522 } 523 524 // processGenerateFromValues generates new values using a sequence if necessary. 525 // If no value was generated, it returns 0. Values are generated only 526 // for cases where none are supplied. 527 func (ins *Insert) processGenerateFromValues( 528 ctx context.Context, 529 vcursor VCursor, 530 bindVars map[string]*querypb.BindVariable, 531 ) (insertID int64, err error) { 532 if ins.Generate == nil { 533 return 0, nil 534 } 535 536 // Scan input values to compute the number of values to generate, and 537 // keep track of where they should be filled. 538 env := evalengine.EnvWithBindVars(bindVars, vcursor.ConnCollation()) 539 resolved, err := env.Evaluate(ins.Generate.Values) 540 if err != nil { 541 return 0, err 542 } 543 count := int64(0) 544 values := resolved.TupleValues() 545 for _, val := range values { 546 if shouldGenerate(val) { 547 count++ 548 } 549 } 550 551 // If generation is needed, generate the requested number of values (as one call). 552 if count != 0 { 553 rss, _, err := vcursor.ResolveDestinations(ctx, ins.Generate.Keyspace.Name, nil, []key.Destination{key.DestinationAnyShard{}}) 554 if err != nil { 555 return 0, err 556 } 557 if len(rss) != 1 { 558 return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "auto sequence generation can happen through single shard only, it is getting routed to %d shards", len(rss)) 559 } 560 bindVars := map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(count)} 561 qr, err := vcursor.ExecuteStandalone(ctx, ins, ins.Generate.Query, bindVars, rss[0]) 562 if err != nil { 563 return 0, err 564 } 565 // If no rows are returned, it's an internal error, and the code 566 // must panic, which will be caught and reported. 567 insertID, err = evalengine.ToInt64(qr.Rows[0][0]) 568 if err != nil { 569 return 0, err 570 } 571 } 572 573 // Fill the holes where no value was supplied. 574 cur := insertID 575 for i, v := range values { 576 if shouldGenerate(v) { 577 bindVars[SeqVarName+strconv.Itoa(i)] = sqltypes.Int64BindVariable(cur) 578 cur++ 579 } else { 580 bindVars[SeqVarName+strconv.Itoa(i)] = sqltypes.ValueBindVariable(v) 581 } 582 } 583 return insertID, nil 584 } 585 586 // processGenerateFromRows generates new values using a sequence if necessary. 587 // If no value was generated, it returns 0. Values are generated only 588 // for cases where none are supplied. 589 func (ins *Insert) processGenerateFromRows( 590 ctx context.Context, 591 vcursor VCursor, 592 rows []sqltypes.Row, 593 ) (insertID int64, err error) { 594 if ins.Generate == nil { 595 return 0, nil 596 } 597 var count int64 598 offset := ins.Generate.Offset 599 genColPresent := offset < len(rows[0]) 600 if genColPresent { 601 for _, val := range rows { 602 if val[offset].IsNull() { 603 count++ 604 } 605 } 606 } else { 607 count = int64(len(rows)) 608 } 609 610 if count == 0 { 611 return 0, nil 612 } 613 614 // If generation is needed, generate the requested number of values (as one call). 615 rss, _, err := vcursor.ResolveDestinations(ctx, ins.Generate.Keyspace.Name, nil, []key.Destination{key.DestinationAnyShard{}}) 616 if err != nil { 617 return 0, err 618 } 619 if len(rss) != 1 { 620 return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "auto sequence generation can happen through single shard only, it is getting routed to %d shards", len(rss)) 621 } 622 bindVars := map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(count)} 623 qr, err := vcursor.ExecuteStandalone(ctx, ins, ins.Generate.Query, bindVars, rss[0]) 624 if err != nil { 625 return 0, err 626 } 627 // If no rows are returned, it's an internal error, and the code 628 // must panic, which will be caught and reported. 629 insertID, err = evalengine.ToInt64(qr.Rows[0][0]) 630 if err != nil { 631 return 0, err 632 } 633 634 used := insertID 635 for idx, val := range rows { 636 if genColPresent { 637 if val[offset].IsNull() { 638 val[offset] = sqltypes.NewInt64(used) 639 used++ 640 } 641 } else { 642 rows[idx] = append(val, sqltypes.NewInt64(used)) 643 used++ 644 } 645 } 646 647 return insertID, nil 648 } 649 650 // getInsertShardedRoute performs all the vindex related work 651 // and returns a map of shard to queries. 652 // Using the primary vindex, it computes the target keyspace ids. 653 // For owned vindexes, it creates entries. 654 // For unowned vindexes with no input values, it reverse maps. 655 // For unowned vindexes with values, it validates. 656 // If it's an IGNORE or ON DUPLICATE key insert, it drops unroutable rows. 657 func (ins *Insert) getInsertShardedRoute( 658 ctx context.Context, 659 vcursor VCursor, 660 bindVars map[string]*querypb.BindVariable, 661 ) ([]*srvtopo.ResolvedShard, []*querypb.BoundQuery, error) { 662 // vindexRowsValues builds the values of all vindex columns. 663 // the 3-d structure indexes are colVindex, row, col. Note that 664 // ins.Values indexes are colVindex, col, row. So, the conversion 665 // involves a transpose. 666 // The reason we need to transpose is that all the Vindex APIs 667 // require inputs in that format. 668 vindexRowsValues := make([][]sqltypes.Row, len(ins.VindexValues)) 669 rowCount := 0 670 env := evalengine.EnvWithBindVars(bindVars, vcursor.ConnCollation()) 671 colVindexes := ins.ColVindexes 672 if colVindexes == nil { 673 colVindexes = ins.Table.ColumnVindexes 674 } 675 for vIdx, vColValues := range ins.VindexValues { 676 if len(vColValues) != len(colVindexes[vIdx].Columns) { 677 return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] supplied vindex column values don't match vschema: %v %v", vColValues, colVindexes[vIdx].Columns) 678 } 679 for colIdx, colValues := range vColValues { 680 rowsResolvedValues := make(sqltypes.Row, 0, len(colValues)) 681 for _, colValue := range colValues { 682 result, err := env.Evaluate(colValue) 683 if err != nil { 684 return nil, nil, err 685 } 686 rowsResolvedValues = append(rowsResolvedValues, result.Value()) 687 } 688 // This is the first iteration: allocate for transpose. 689 if colIdx == 0 { 690 if len(rowsResolvedValues) == 0 { 691 return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] rowcount is zero for inserts: %v", rowsResolvedValues) 692 } 693 if rowCount == 0 { 694 rowCount = len(rowsResolvedValues) 695 } 696 if rowCount != len(rowsResolvedValues) { 697 return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] uneven row values for inserts: %d %d", rowCount, len(rowsResolvedValues)) 698 } 699 vindexRowsValues[vIdx] = make([]sqltypes.Row, rowCount) 700 } 701 // Perform the transpose. 702 for rowNum, colVal := range rowsResolvedValues { 703 vindexRowsValues[vIdx][rowNum] = append(vindexRowsValues[vIdx][rowNum], colVal) 704 } 705 } 706 } 707 708 // The output from the following 'process' functions is a list of 709 // keyspace ids. For regular inserts, a failure to find a route 710 // results in an error. For 'ignore' type inserts, the keyspace 711 // id is returned as nil, which is used later to drop the corresponding rows. 712 if len(vindexRowsValues) == 0 || len(colVindexes) == 0 { 713 return nil, nil, vterrors.NewErrorf(vtrpcpb.Code_FAILED_PRECONDITION, vterrors.RequiresPrimaryKey, vterrors.PrimaryVindexNotSet, ins.Table.Name) 714 } 715 keyspaceIDs, err := ins.processPrimary(ctx, vcursor, vindexRowsValues[0], colVindexes[0]) 716 if err != nil { 717 return nil, nil, err 718 } 719 720 for vIdx := 1; vIdx < len(colVindexes); vIdx++ { 721 colVindex := colVindexes[vIdx] 722 var err error 723 if colVindex.Owned { 724 err = ins.processOwned(ctx, vcursor, vindexRowsValues[vIdx], colVindex, keyspaceIDs) 725 } else { 726 err = ins.processUnowned(ctx, vcursor, vindexRowsValues[vIdx], colVindex, keyspaceIDs) 727 } 728 if err != nil { 729 return nil, nil, err 730 } 731 } 732 733 // Build 3-d bindvars. Skip rows with nil keyspace ids in case 734 // we're executing an insert ignore. 735 for vIdx, colVindex := range colVindexes { 736 for rowNum, rowColumnKeys := range vindexRowsValues[vIdx] { 737 if keyspaceIDs[rowNum] == nil { 738 // InsertIgnore: skip the row. 739 continue 740 } 741 for colIdx, vindexKey := range rowColumnKeys { 742 col := colVindex.Columns[colIdx] 743 name := InsertVarName(col, rowNum) 744 bindVars[name] = sqltypes.ValueBindVariable(vindexKey) 745 } 746 } 747 } 748 749 // We need to know the keyspace ids and the Mids associated with 750 // each RSS. So we pass the ksid indexes in as ids, and get them back 751 // as values. We also skip nil KeyspaceIds, no need to resolve them. 752 var indexes []*querypb.Value 753 var destinations []key.Destination 754 for i, ksid := range keyspaceIDs { 755 if ksid != nil { 756 indexes = append(indexes, &querypb.Value{ 757 Value: strconv.AppendInt(nil, int64(i), 10), 758 }) 759 destinations = append(destinations, key.DestinationKeyspaceID(ksid)) 760 } 761 } 762 if len(destinations) == 0 { 763 // In this case, all we have is nil KeyspaceIds, we don't do 764 // anything at all. 765 return nil, nil, nil 766 } 767 768 rss, indexesPerRss, err := vcursor.ResolveDestinations(ctx, ins.Keyspace.Name, indexes, destinations) 769 if err != nil { 770 return nil, nil, err 771 } 772 773 queries := make([]*querypb.BoundQuery, len(rss)) 774 for i := range rss { 775 var mids []string 776 for _, indexValue := range indexesPerRss[i] { 777 index, _ := strconv.ParseInt(string(indexValue.Value), 0, 64) 778 if keyspaceIDs[index] != nil { 779 mids = append(mids, ins.Mid[index]) 780 } 781 } 782 rewritten := ins.Prefix + strings.Join(mids, ",") + ins.Suffix 783 queries[i] = &querypb.BoundQuery{ 784 Sql: rewritten, 785 BindVariables: bindVars, 786 } 787 } 788 789 return rss, queries, nil 790 } 791 792 // processPrimary maps the primary vindex values to the keyspace ids. 793 func (ins *Insert) processPrimary(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex) ([]ksID, error) { 794 destinations, err := vindexes.Map(ctx, colVindex.Vindex, vcursor, vindexColumnsKeys) 795 if err != nil { 796 return nil, err 797 } 798 799 keyspaceIDs := make([]ksID, len(destinations)) 800 for i, destination := range destinations { 801 switch d := destination.(type) { 802 case key.DestinationKeyspaceID: 803 // This is a single keyspace id, we're good. 804 keyspaceIDs[i] = d 805 case key.DestinationNone: 806 // No valid keyspace id, we may return an error. 807 if !ins.Ignore { 808 return nil, fmt.Errorf("could not map %v to a keyspace id", vindexColumnsKeys[i]) 809 } 810 default: 811 return nil, fmt.Errorf("could not map %v to a unique keyspace id: %v", vindexColumnsKeys[i], destination) 812 } 813 } 814 815 return keyspaceIDs, nil 816 } 817 818 // processOwned creates vindex entries for the values of an owned column. 819 func (ins *Insert) processOwned(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex, ksids []ksID) error { 820 if !ins.Ignore { 821 return colVindex.Vindex.(vindexes.Lookup).Create(ctx, vcursor, vindexColumnsKeys, ksids, false /* ignoreMode */) 822 } 823 824 // InsertIgnore 825 var createIndexes []int 826 var createKeys []sqltypes.Row 827 var createKsids []ksID 828 829 for rowNum, rowColumnKeys := range vindexColumnsKeys { 830 if ksids[rowNum] == nil { 831 continue 832 } 833 createIndexes = append(createIndexes, rowNum) 834 createKeys = append(createKeys, rowColumnKeys) 835 createKsids = append(createKsids, ksids[rowNum]) 836 } 837 if createKeys == nil { 838 return nil 839 } 840 841 err := colVindex.Vindex.(vindexes.Lookup).Create(ctx, vcursor, createKeys, createKsids, true) 842 if err != nil { 843 return err 844 } 845 // After creation, verify that the keys map to the keyspace ids. If not, remove 846 // those that don't map. 847 verified, err := vindexes.Verify(ctx, colVindex.Vindex, vcursor, createKeys, createKsids) 848 if err != nil { 849 return err 850 } 851 for i, v := range verified { 852 if !v { 853 ksids[createIndexes[i]] = nil 854 } 855 } 856 return nil 857 } 858 859 // processUnowned either reverse maps or validates the values for an unowned column. 860 func (ins *Insert) processUnowned(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex, ksids []ksID) error { 861 var reverseIndexes []int 862 var reverseKsids []ksID 863 864 var verifyIndexes []int 865 var verifyKeys []sqltypes.Row 866 var verifyKsids []ksID 867 868 // Check if this VIndex is reversible or not. 869 reversibleVindex, isReversible := colVindex.Vindex.(vindexes.Reversible) 870 871 for rowNum, rowColumnKeys := range vindexColumnsKeys { 872 // If we weren't able to determine a keyspace id from the primary VIndex, skip this row 873 if ksids[rowNum] == nil { 874 continue 875 } 876 877 if rowColumnKeys[0].IsNull() { 878 // If the value of the column is `NULL`, but this is a reversible VIndex, 879 // we will try to generate the value from the keyspace id generated by the primary VIndex. 880 if isReversible { 881 reverseIndexes = append(reverseIndexes, rowNum) 882 reverseKsids = append(reverseKsids, ksids[rowNum]) 883 } 884 885 // Otherwise, don't do anything. Whether `NULL` is a valid value for this column will be 886 // handled by MySQL. 887 } else { 888 // If a value for this column was specified, the keyspace id values from the 889 // secondary VIndex need to be verified against the keyspace id from the primary VIndex 890 verifyIndexes = append(verifyIndexes, rowNum) 891 verifyKeys = append(verifyKeys, rowColumnKeys) 892 verifyKsids = append(verifyKsids, ksids[rowNum]) 893 } 894 } 895 896 // Reverse map values for secondary VIndex columns from the primary VIndex's keyspace id. 897 if reverseKsids != nil { 898 reverseKeys, err := reversibleVindex.ReverseMap(vcursor, reverseKsids) 899 if err != nil { 900 return err 901 } 902 903 for i, reverseKey := range reverseKeys { 904 // Fill the first column with the reverse-mapped value. 905 vindexColumnsKeys[reverseIndexes[i]][0] = reverseKey 906 } 907 } 908 909 // Verify that the keyspace ids generated by the primary and secondary VIndexes match 910 if verifyIndexes != nil { 911 // If values were supplied, we validate against keyspace id. 912 verified, err := vindexes.Verify(ctx, colVindex.Vindex, vcursor, verifyKeys, verifyKsids) 913 if err != nil { 914 return err 915 } 916 917 var mismatchVindexKeys []sqltypes.Row 918 for i, v := range verified { 919 rowNum := verifyIndexes[i] 920 if !v { 921 if !ins.Ignore { 922 mismatchVindexKeys = append(mismatchVindexKeys, vindexColumnsKeys[rowNum]) 923 continue 924 } 925 926 // Skip the whole row if this is a `INSERT IGNORE` or `INSERT ... ON DUPLICATE KEY ...` statement 927 // but the keyspace ids didn't match. 928 ksids[verifyIndexes[i]] = nil 929 } 930 } 931 932 if mismatchVindexKeys != nil { 933 return fmt.Errorf("values %v for column %v does not map to keyspace ids", mismatchVindexKeys, colVindex.Columns) 934 } 935 } 936 937 return nil 938 } 939 940 // InsertVarName returns a name for the bind var for this column. This method is used by the planner and engine, 941 // to make sure they both produce the same names 942 func InsertVarName(col sqlparser.IdentifierCI, rowNum int) string { 943 return fmt.Sprintf("_%s_%d", col.CompliantName(), rowNum) 944 } 945 946 func insertVarOffset(rowNum, colOffset int) string { 947 return fmt.Sprintf("_c%d_%d", rowNum, colOffset) 948 } 949 950 func (ins *Insert) description() PrimitiveDescription { 951 other := map[string]any{ 952 "Query": ins.Query, 953 "TableName": ins.GetTableName(), 954 "MultiShardAutocommit": ins.MultiShardAutocommit, 955 "QueryTimeout": ins.QueryTimeout, 956 } 957 958 if len(ins.VindexValues) > 0 { 959 valuesOffsets := map[string]string{} 960 for idx, ints := range ins.VindexValues { 961 if len(ins.ColVindexes) < idx { 962 panic("ins.ColVindexes and ins.VindexValueOffset do not line up") 963 } 964 vindex := ins.ColVindexes[idx] 965 var res []string 966 for _, exprs := range ints { 967 var this []string 968 for _, expr := range exprs { 969 this = append(this, evalengine.FormatExpr(expr)) 970 } 971 res = append(res, strings.Join(this, ", ")) 972 } 973 974 valuesOffsets[vindex.Name] = strings.Join(res, ", ") 975 } 976 other["VindexValues"] = valuesOffsets 977 } 978 979 if ins.Generate != nil && ins.Generate.Values == nil { 980 other["AutoIncrement"] = fmt.Sprintf("%s:%d", ins.Generate.Keyspace.Name, ins.Generate.Offset) 981 } 982 983 if len(ins.VindexValueOffset) > 0 { 984 valuesOffsets := map[string]string{} 985 for idx, ints := range ins.VindexValueOffset { 986 if len(ins.ColVindexes) < idx { 987 panic("ins.ColVindexes and ins.VindexValueOffset do not line up") 988 } 989 vindex := ins.ColVindexes[idx] 990 marshal, _ := json.Marshal(ints) 991 valuesOffsets[vindex.Name] = string(marshal) 992 } 993 other["VindexOffsetFromSelect"] = valuesOffsets 994 } 995 if ins.Ignore { 996 other["InsertIgnore"] = true 997 } 998 return PrimitiveDescription{ 999 OperatorType: "Insert", 1000 Keyspace: ins.Keyspace, 1001 Variant: ins.Opcode.String(), 1002 TargetTabletType: topodatapb.TabletType_PRIMARY, 1003 Other: other, 1004 } 1005 } 1006 1007 func (ins *Insert) insertIntoUnshardedTable(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, result *sqltypes.Result) (int64, *sqltypes.Result, error) { 1008 query := ins.getInsertQueryForUnsharded(result, bindVars) 1009 return ins.executeUnshardedTableQuery(ctx, vcursor, bindVars, query) 1010 } 1011 1012 func (ins *Insert) executeUnshardedTableQuery(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, query string) (int64, *sqltypes.Result, error) { 1013 insertID, err := ins.processGenerateFromValues(ctx, vcursor, bindVars) 1014 if err != nil { 1015 return 0, nil, err 1016 } 1017 1018 rss, _, err := vcursor.ResolveDestinations(ctx, ins.Keyspace.Name, nil, []key.Destination{key.DestinationAllShards{}}) 1019 if err != nil { 1020 return 0, nil, err 1021 } 1022 if len(rss) != 1 { 1023 return 0, nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) 1024 } 1025 err = allowOnlyPrimary(rss...) 1026 if err != nil { 1027 return 0, nil, err 1028 } 1029 qr, err := execShard(ctx, ins, vcursor, query, bindVars, rss[0], true, true /* canAutocommit */) 1030 if err != nil { 1031 return 0, nil, err 1032 } 1033 1034 // If processGenerateFromValues generated new values, it supercedes 1035 // any ids that MySQL might have generated. If both generated 1036 // values, we don't return an error because this behavior 1037 // is required to support migration. 1038 if insertID != 0 { 1039 qr.InsertID = uint64(insertID) 1040 } else { 1041 insertID = int64(qr.InsertID) 1042 } 1043 return insertID, qr, nil 1044 }