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  }