vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/dml.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  	"fmt"
    22  	"sort"
    23  	"strings"
    24  
    25  	"vitess.io/vitess/go/sqltypes"
    26  	"vitess.io/vitess/go/vt/key"
    27  	"vitess.io/vitess/go/vt/srvtopo"
    28  	"vitess.io/vitess/go/vt/topo/topoproto"
    29  	"vitess.io/vitess/go/vt/vterrors"
    30  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    31  
    32  	querypb "vitess.io/vitess/go/vt/proto/query"
    33  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    34  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    35  )
    36  
    37  // DML contains the common elements between Update and Delete plans
    38  type DML struct {
    39  	// Query specifies the query to be executed.
    40  	Query string
    41  
    42  	// KsidVindex is primary Vindex
    43  	KsidVindex vindexes.Vindex
    44  
    45  	// KsidLength is number of columns that represents KsidVindex
    46  	KsidLength int
    47  
    48  	// Table specifies the table for the update.
    49  	Table []*vindexes.Table
    50  
    51  	// OwnedVindexQuery is used for updating changes in lookup vindexes.
    52  	OwnedVindexQuery string
    53  
    54  	// Option to override the standard behavior and allow a multi-shard update
    55  	// to use single round trip autocommit.
    56  	MultiShardAutocommit bool
    57  
    58  	// QueryTimeout contains the optional timeout (in milliseconds) to apply to this query
    59  	QueryTimeout int
    60  
    61  	// RoutingParameters parameters required for query routing.
    62  	*RoutingParameters
    63  
    64  	txNeeded
    65  }
    66  
    67  // NewDML returns and empty initialized DML struct.
    68  func NewDML() *DML {
    69  	return &DML{RoutingParameters: &RoutingParameters{}}
    70  }
    71  
    72  func (dml *DML) execUnsharded(ctx context.Context, primitive Primitive, vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) (*sqltypes.Result, error) {
    73  	return execShard(ctx, primitive, vcursor, dml.Query, bindVars, rss[0], true /* rollbackOnError */, true /* canAutocommit */)
    74  }
    75  
    76  func (dml *DML) execMultiDestination(ctx context.Context, primitive Primitive, vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard, dmlSpecialFunc func(context.Context, VCursor, map[string]*querypb.BindVariable, []*srvtopo.ResolvedShard) error) (*sqltypes.Result, error) {
    77  	if len(rss) == 0 {
    78  		return &sqltypes.Result{}, nil
    79  	}
    80  	err := dmlSpecialFunc(ctx, vcursor, bindVars, rss)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	queries := make([]*querypb.BoundQuery, len(rss))
    85  	for i := range rss {
    86  		queries[i] = &querypb.BoundQuery{
    87  			Sql:           dml.Query,
    88  			BindVariables: bindVars,
    89  		}
    90  	}
    91  	return execMultiShard(ctx, primitive, vcursor, rss, queries, dml.MultiShardAutocommit)
    92  }
    93  
    94  // RouteType returns a description of the query routing type used by the primitive
    95  func (dml *DML) RouteType() string {
    96  	return dml.Opcode.String()
    97  }
    98  
    99  // GetKeyspaceName specifies the Keyspace that this primitive routes to.
   100  func (dml *DML) GetKeyspaceName() string {
   101  	return dml.Keyspace.Name
   102  }
   103  
   104  // GetTableName specifies the table that this primitive routes to.
   105  func (dml *DML) GetTableName() string {
   106  	if dml.Table != nil {
   107  		tableNameMap := map[string]any{}
   108  		for _, table := range dml.Table {
   109  			tableNameMap[table.Name.String()] = nil
   110  		}
   111  
   112  		var tableNames []string
   113  		for name := range tableNameMap {
   114  			tableNames = append(tableNames, name)
   115  		}
   116  		sort.Strings(tableNames)
   117  
   118  		return strings.Join(tableNames, ", ")
   119  	}
   120  	return ""
   121  }
   122  
   123  // GetSingleTable returns single table used in dml.
   124  func (dml *DML) GetSingleTable() (*vindexes.Table, error) {
   125  	if len(dml.Table) > 1 {
   126  		return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported dml on complex table expression")
   127  	}
   128  	return dml.Table[0], nil
   129  }
   130  
   131  func allowOnlyPrimary(rss ...*srvtopo.ResolvedShard) error {
   132  	for _, rs := range rss {
   133  		if rs != nil && rs.Target.TabletType != topodatapb.TabletType_PRIMARY {
   134  			return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "supported only for primary tablet type, current type: %v", topoproto.TabletTypeLString(rs.Target.TabletType))
   135  		}
   136  	}
   137  	return nil
   138  }
   139  
   140  func execMultiShard(ctx context.Context, primitive Primitive, vcursor VCursor, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, multiShardAutoCommit bool) (*sqltypes.Result, error) {
   141  	autocommit := (len(rss) == 1 || multiShardAutoCommit) && vcursor.AutocommitApproval()
   142  	result, errs := vcursor.ExecuteMultiShard(ctx, primitive, rss, queries, true /* rollbackOnError */, autocommit)
   143  	return result, vterrors.Aggregate(errs)
   144  }
   145  
   146  func resolveKeyspaceID(ctx context.Context, vcursor VCursor, vindex vindexes.Vindex, vindexKey []sqltypes.Value) ([]byte, error) {
   147  	var destinations []key.Destination
   148  	var err error
   149  	switch vdx := vindex.(type) {
   150  	case vindexes.MultiColumn:
   151  		destinations, err = vdx.Map(ctx, vcursor, [][]sqltypes.Value{vindexKey})
   152  	case vindexes.SingleColumn:
   153  		destinations, err = vdx.Map(ctx, vcursor, vindexKey)
   154  	}
   155  
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	switch ksid := destinations[0].(type) {
   160  	case key.DestinationKeyspaceID:
   161  		return ksid, nil
   162  	case key.DestinationNone:
   163  		return nil, nil
   164  	default:
   165  		return nil, fmt.Errorf("cannot map vindex to unique keyspace id: %v", destinations[0])
   166  	}
   167  }