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 }