vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/lock.go (about) 1 /* 2 Copyright 2020 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 23 "vitess.io/vitess/go/vt/vtgate/evalengine" 24 25 "vitess.io/vitess/go/vt/srvtopo" 26 27 querypb "vitess.io/vitess/go/vt/proto/query" 28 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 29 30 "vitess.io/vitess/go/sqltypes" 31 "vitess.io/vitess/go/vt/key" 32 "vitess.io/vitess/go/vt/sqlparser" 33 "vitess.io/vitess/go/vt/vterrors" 34 "vitess.io/vitess/go/vt/vtgate/vindexes" 35 ) 36 37 var _ Primitive = (*Lock)(nil) 38 39 // Lock primitive will execute sql containing lock functions 40 type Lock struct { 41 // Keyspace specifies the keyspace to send the query to. 42 Keyspace *vindexes.Keyspace 43 44 // TargetDestination specifies an explicit target destination to send the query to. 45 TargetDestination key.Destination 46 47 FieldQuery string 48 49 LockFunctions []*LockFunc 50 51 noInputs 52 53 noTxNeeded 54 } 55 56 type LockFunc struct { 57 Typ *sqlparser.LockingFunc 58 Name evalengine.Expr 59 } 60 61 // RouteType is part of the Primitive interface 62 func (l *Lock) RouteType() string { 63 return "lock" 64 } 65 66 // GetKeyspaceName is part of the Primitive interface 67 func (l *Lock) GetKeyspaceName() string { 68 return l.Keyspace.Name 69 } 70 71 // GetTableName is part of the Primitive interface 72 func (l *Lock) GetTableName() string { 73 return "dual" 74 } 75 76 // TryExecute is part of the Primitive interface 77 func (l *Lock) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { 78 return l.execLock(ctx, vcursor, bindVars) 79 } 80 81 func (l *Lock) execLock(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 82 rss, _, err := vcursor.ResolveDestinations(ctx, l.Keyspace.Name, nil, []key.Destination{l.TargetDestination}) 83 if err != nil { 84 return nil, err 85 } 86 if len(rss) != 1 { 87 return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "lock query can be routed to single shard only: %v", rss) 88 } 89 90 env := &evalengine.ExpressionEnv{BindVars: bindVars} 91 var fields []*querypb.Field 92 var rrow sqltypes.Row 93 for _, lf := range l.LockFunctions { 94 var lName string 95 if lf.Name != nil { 96 er, err := env.Evaluate(lf.Name) 97 if err != nil { 98 return nil, err 99 } 100 lName = er.Value().ToString() 101 } 102 qr, err := lf.execLock(ctx, vcursor, bindVars, rss[0]) 103 if err != nil { 104 return nil, err 105 } 106 fields = append(fields, qr.Fields...) 107 lockRes := qr.Rows[0] 108 rrow = append(rrow, lockRes...) 109 110 switch lf.Typ.Type { 111 case sqlparser.IsFreeLock, sqlparser.IsUsedLock: 112 case sqlparser.GetLock: 113 if lockRes[0].ToString() == "1" { 114 vcursor.Session().AddAdvisoryLock(lName) 115 } 116 case sqlparser.ReleaseAllLocks: 117 err = vcursor.ReleaseLock(ctx) 118 if err != nil { 119 return nil, err 120 } 121 case sqlparser.ReleaseLock: 122 // TODO: do not execute if lock not taken. 123 if lockRes[0].ToString() == "1" { 124 vcursor.Session().RemoveAdvisoryLock(lName) 125 } 126 if !vcursor.Session().AnyAdvisoryLockTaken() { 127 err = vcursor.ReleaseLock(ctx) 128 if err != nil { 129 return nil, err 130 } 131 } 132 } 133 } 134 return &sqltypes.Result{ 135 Fields: fields, 136 Rows: []sqltypes.Row{rrow}, 137 }, nil 138 } 139 140 func (lf *LockFunc) execLock(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) { 141 boundQuery := &querypb.BoundQuery{ 142 Sql: fmt.Sprintf("select %s from dual", sqlparser.String(lf.Typ)), 143 BindVariables: bindVars, 144 } 145 qr, err := vcursor.ExecuteLock(ctx, rs, boundQuery, lf.Typ.Type) 146 if err != nil { 147 return nil, err 148 } 149 if len(qr.Rows) != 1 && len(qr.Fields) != 1 { 150 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected rows or fields returned for the lock function: %v", lf.Typ.Type) 151 } 152 return qr, nil 153 } 154 155 // TryStreamExecute is part of the Primitive interface 156 func (l *Lock) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { 157 qr, err := l.TryExecute(ctx, vcursor, bindVars, wantfields) 158 if err != nil { 159 return err 160 } 161 return callback(qr) 162 } 163 164 // GetFields is part of the Primitive interface 165 func (l *Lock) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 166 rss, _, err := vcursor.ResolveDestinations(ctx, l.Keyspace.Name, nil, []key.Destination{l.TargetDestination}) 167 if err != nil { 168 return nil, err 169 } 170 if len(rss) != 1 { 171 return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "lock query can be routed to single shard only: %v", rss) 172 } 173 boundQuery := []*querypb.BoundQuery{{ 174 Sql: l.FieldQuery, 175 BindVariables: bindVars, 176 }} 177 qr, errs := vcursor.ExecuteMultiShard(ctx, l, rss, boundQuery, false, true) 178 if len(errs) > 0 { 179 return nil, vterrors.Aggregate(errs) 180 } 181 return qr, nil 182 } 183 184 func (l *Lock) description() PrimitiveDescription { 185 other := map[string]any{ 186 "FieldQuery": l.FieldQuery, 187 } 188 var lf []string 189 for _, f := range l.LockFunctions { 190 lf = append(lf, sqlparser.String(f.Typ)) 191 } 192 other["lock_func"] = lf 193 return PrimitiveDescription{ 194 OperatorType: "Lock", 195 Keyspace: l.Keyspace, 196 TargetDestination: l.TargetDestination, 197 Other: other, 198 } 199 }