vitess.io/vitess@v0.16.2/go/vt/vttablet/endtoend/framework/client.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 framework 18 19 import ( 20 "context" 21 "errors" 22 "time" 23 24 "google.golang.org/protobuf/proto" 25 26 "vitess.io/vitess/go/sqltypes" 27 "vitess.io/vitess/go/vt/callerid" 28 "vitess.io/vitess/go/vt/vttablet/tabletserver" 29 30 querypb "vitess.io/vitess/go/vt/proto/query" 31 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 32 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 33 ) 34 35 // QueryClient provides a convenient wrapper for TabletServer's query service. 36 // It's not thread safe, but you can create multiple clients that point to the 37 // same server. 38 type QueryClient struct { 39 ctx context.Context 40 target *querypb.Target 41 server *tabletserver.TabletServer 42 transactionID int64 43 reservedID int64 44 sessionStateChanges string 45 } 46 47 // NewClient creates a new client for Server. 48 func NewClient() *QueryClient { 49 return &QueryClient{ 50 ctx: callerid.NewContext( 51 context.Background(), 52 &vtrpcpb.CallerID{}, 53 &querypb.VTGateCallerID{Username: "dev"}, 54 ), 55 target: Target, 56 server: Server, 57 } 58 } 59 60 // NewClientWithTabletType creates a new client for Server with the provided tablet type. 61 func NewClientWithTabletType(tabletType topodatapb.TabletType) *QueryClient { 62 targetCopy := proto.Clone(Target).(*querypb.Target) 63 targetCopy.TabletType = tabletType 64 return &QueryClient{ 65 ctx: callerid.NewContext( 66 context.Background(), 67 &vtrpcpb.CallerID{}, 68 &querypb.VTGateCallerID{Username: "dev"}, 69 ), 70 target: targetCopy, 71 server: Server, 72 } 73 } 74 75 // NewClientWithContext creates a new client for Server with the provided context. 76 func NewClientWithContext(ctx context.Context) *QueryClient { 77 return &QueryClient{ 78 ctx: ctx, 79 target: Target, 80 server: Server, 81 } 82 } 83 84 // Begin begins a transaction. 85 func (client *QueryClient) Begin(clientFoundRows bool) error { 86 if client.transactionID != 0 { 87 return errors.New("already in transaction") 88 } 89 var options *querypb.ExecuteOptions 90 if clientFoundRows { 91 options = &querypb.ExecuteOptions{ClientFoundRows: clientFoundRows} 92 } 93 state, err := client.server.Begin(client.ctx, client.target, options) 94 if err != nil { 95 return err 96 } 97 client.transactionID = state.TransactionID 98 client.sessionStateChanges = state.SessionStateChanges 99 return nil 100 } 101 102 // Commit commits the current transaction. 103 func (client *QueryClient) Commit() error { 104 defer func() { client.transactionID = 0 }() 105 rID, err := client.server.Commit(client.ctx, client.target, client.transactionID) 106 client.reservedID = rID 107 if err != nil { 108 return err 109 } 110 return nil 111 } 112 113 // Rollback rolls back the current transaction. 114 func (client *QueryClient) Rollback() error { 115 defer func() { client.transactionID = 0 }() 116 rID, err := client.server.Rollback(client.ctx, client.target, client.transactionID) 117 client.reservedID = rID 118 if err != nil { 119 return err 120 } 121 return nil 122 } 123 124 // Prepare executes a prepare on the current transaction. 125 func (client *QueryClient) Prepare(dtid string) error { 126 defer func() { client.transactionID = 0 }() 127 return client.server.Prepare(client.ctx, client.target, client.transactionID, dtid) 128 } 129 130 // CommitPrepared commits a prepared transaction. 131 func (client *QueryClient) CommitPrepared(dtid string) error { 132 return client.server.CommitPrepared(client.ctx, client.target, dtid) 133 } 134 135 // RollbackPrepared rollsback a prepared transaction. 136 func (client *QueryClient) RollbackPrepared(dtid string, originalID int64) error { 137 return client.server.RollbackPrepared(client.ctx, client.target, dtid, originalID) 138 } 139 140 // CreateTransaction issues a CreateTransaction to TabletServer. 141 func (client *QueryClient) CreateTransaction(dtid string, participants []*querypb.Target) error { 142 return client.server.CreateTransaction(client.ctx, client.target, dtid, participants) 143 } 144 145 // StartCommit issues a StartCommit to TabletServer for the current transaction. 146 func (client *QueryClient) StartCommit(dtid string) error { 147 defer func() { client.transactionID = 0 }() 148 return client.server.StartCommit(client.ctx, client.target, client.transactionID, dtid) 149 } 150 151 // SetRollback issues a SetRollback to TabletServer. 152 func (client *QueryClient) SetRollback(dtid string, transactionID int64) error { 153 return client.server.SetRollback(client.ctx, client.target, dtid, client.transactionID) 154 } 155 156 // ConcludeTransaction issues a ConcludeTransaction to TabletServer. 157 func (client *QueryClient) ConcludeTransaction(dtid string) error { 158 return client.server.ConcludeTransaction(client.ctx, client.target, dtid) 159 } 160 161 // ReadTransaction returns the transaction metadata. 162 func (client *QueryClient) ReadTransaction(dtid string) (*querypb.TransactionMetadata, error) { 163 return client.server.ReadTransaction(client.ctx, client.target, dtid) 164 } 165 166 // SetServingType is for testing transitions. 167 // It currently supports only primary->replica and back. 168 func (client *QueryClient) SetServingType(tabletType topodatapb.TabletType) error { 169 err := client.server.SetServingType(tabletType, time.Time{}, true /* serving */, "" /* reason */) 170 // Wait for TwoPC transition, if necessary 171 client.server.TwoPCEngineWait() 172 return err 173 } 174 175 // Execute executes a query. 176 func (client *QueryClient) Execute(query string, bindvars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 177 return client.ExecuteWithOptions(query, bindvars, &querypb.ExecuteOptions{ 178 IncludedFields: querypb.ExecuteOptions_ALL, 179 }) 180 } 181 182 // BeginExecute performs a BeginExecute. 183 func (client *QueryClient) BeginExecute(query string, bindvars map[string]*querypb.BindVariable, preQueries []string) (*sqltypes.Result, error) { 184 if client.transactionID != 0 { 185 return nil, errors.New("already in transaction") 186 } 187 state, qr, err := client.server.BeginExecute( 188 client.ctx, 189 client.target, 190 preQueries, 191 query, 192 bindvars, 193 client.reservedID, 194 &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}, 195 ) 196 client.transactionID = state.TransactionID 197 client.sessionStateChanges = state.SessionStateChanges 198 if err != nil { 199 return nil, err 200 } 201 return qr, nil 202 } 203 204 // ExecuteWithOptions executes a query using 'options'. 205 func (client *QueryClient) ExecuteWithOptions(query string, bindvars map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { 206 return client.server.Execute( 207 client.ctx, 208 client.target, 209 query, 210 bindvars, 211 client.transactionID, 212 client.reservedID, 213 options, 214 ) 215 } 216 217 // StreamExecute executes a query & returns the results. 218 func (client *QueryClient) StreamExecute(query string, bindvars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 219 return client.StreamExecuteWithOptions(query, bindvars, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}) 220 } 221 222 // StreamExecuteWithOptions executes a query & returns the results using 'options'. 223 func (client *QueryClient) StreamExecuteWithOptions(query string, bindvars map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { 224 result := &sqltypes.Result{} 225 err := client.server.StreamExecute(client.ctx, 226 client.target, 227 query, 228 bindvars, 229 client.transactionID, 230 client.reservedID, 231 options, 232 func(res *sqltypes.Result) error { 233 if result.Fields == nil { 234 result.Fields = res.Fields 235 } 236 result.Rows = append(result.Rows, res.Rows...) 237 return nil 238 }) 239 if err != nil { 240 return nil, err 241 } 242 return result, nil 243 } 244 245 // StreamBeginExecuteWithOptions starts a tx and executes a query using 'options', returning the results . 246 func (client *QueryClient) StreamBeginExecuteWithOptions(query string, preQueries []string, bindvars map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { 247 result := &sqltypes.Result{} 248 state, err := client.server.BeginStreamExecute( 249 client.ctx, 250 client.target, 251 preQueries, 252 query, 253 bindvars, 254 client.reservedID, 255 options, 256 func(res *sqltypes.Result) error { 257 if result.Fields == nil { 258 result.Fields = res.Fields 259 } 260 result.Rows = append(result.Rows, res.Rows...) 261 return nil 262 }, 263 ) 264 client.transactionID = state.TransactionID 265 client.sessionStateChanges = state.SessionStateChanges 266 if err != nil { 267 return nil, err 268 } 269 return result, nil 270 } 271 272 // Stream streams the results of a query. 273 func (client *QueryClient) Stream(query string, bindvars map[string]*querypb.BindVariable, sendFunc func(*sqltypes.Result) error) error { 274 return client.server.StreamExecute(client.ctx, client.target, query, bindvars, 0, 0, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}, sendFunc) 275 } 276 277 // MessageStream streams messages from the message table. 278 func (client *QueryClient) MessageStream(name string, callback func(*sqltypes.Result) error) (err error) { 279 return client.server.MessageStream(client.ctx, client.target, name, callback) 280 } 281 282 // MessageAck acks messages 283 func (client *QueryClient) MessageAck(name string, ids []string) (int64, error) { 284 bids := make([]*querypb.Value, 0, len(ids)) 285 for _, id := range ids { 286 bids = append(bids, &querypb.Value{ 287 Type: sqltypes.VarChar, 288 Value: []byte(id), 289 }) 290 } 291 return client.server.MessageAck(client.ctx, client.target, name, bids) 292 } 293 294 // ReserveExecute performs a ReserveExecute. 295 func (client *QueryClient) ReserveExecute(query string, preQueries []string, bindvars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 296 if client.reservedID != 0 { 297 return nil, errors.New("already reserved a connection") 298 } 299 state, qr, err := client.server.ReserveExecute(client.ctx, client.target, preQueries, query, bindvars, client.transactionID, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}) 300 client.reservedID = state.ReservedID 301 if err != nil { 302 return nil, err 303 } 304 return qr, nil 305 } 306 307 // ReserveStreamExecute performs a ReserveStreamExecute. 308 func (client *QueryClient) ReserveStreamExecute(query string, preQueries []string, bindvars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 309 if client.reservedID != 0 { 310 return nil, errors.New("already reserved a connection") 311 } 312 result := &sqltypes.Result{} 313 state, err := client.server.ReserveStreamExecute(client.ctx, client.target, preQueries, query, bindvars, client.transactionID, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}, 314 func(res *sqltypes.Result) error { 315 if result.Fields == nil { 316 result.Fields = res.Fields 317 } 318 result.Rows = append(result.Rows, res.Rows...) 319 return nil 320 }) 321 client.reservedID = state.ReservedID 322 if err != nil { 323 return nil, err 324 } 325 return result, nil 326 } 327 328 // ReserveBeginExecute performs a ReserveBeginExecute. 329 func (client *QueryClient) ReserveBeginExecute(query string, preQueries []string, postBeginQueries []string, bindvars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 330 if client.reservedID != 0 { 331 return nil, errors.New("already reserved a connection") 332 } 333 if client.transactionID != 0 { 334 return nil, errors.New("already in transaction") 335 } 336 state, qr, err := client.server.ReserveBeginExecute(client.ctx, client.target, preQueries, postBeginQueries, query, bindvars, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}) 337 client.transactionID = state.TransactionID 338 client.reservedID = state.ReservedID 339 client.sessionStateChanges = state.SessionStateChanges 340 if err != nil { 341 return nil, err 342 } 343 return qr, nil 344 } 345 346 // ReserveBeginStreamExecute performs a ReserveBeginStreamExecute. 347 func (client *QueryClient) ReserveBeginStreamExecute(query string, preQueries []string, postBeginQueries []string, bindvars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 348 if client.reservedID != 0 { 349 return nil, errors.New("already reserved a connection") 350 } 351 if client.transactionID != 0 { 352 return nil, errors.New("already in transaction") 353 } 354 result := &sqltypes.Result{} 355 state, err := client.server.ReserveBeginStreamExecute(client.ctx, client.target, preQueries, postBeginQueries, query, bindvars, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}, 356 func(res *sqltypes.Result) error { 357 if result.Fields == nil { 358 result.Fields = res.Fields 359 } 360 result.Rows = append(result.Rows, res.Rows...) 361 return nil 362 }) 363 client.transactionID = state.TransactionID 364 client.reservedID = state.ReservedID 365 client.sessionStateChanges = state.SessionStateChanges 366 if err != nil { 367 return nil, err 368 } 369 return result, nil 370 } 371 372 // Release performs a Release. 373 func (client *QueryClient) Release() error { 374 err := client.server.Release(client.ctx, client.target, client.transactionID, client.reservedID) 375 client.reservedID = 0 376 client.transactionID = 0 377 if err != nil { 378 return err 379 } 380 return nil 381 } 382 383 // TransactionID returns transactionID 384 func (client *QueryClient) TransactionID() int64 { 385 return client.transactionID 386 } 387 388 // ReservedID returns reservedID 389 func (client *QueryClient) ReservedID() int64 { 390 return client.reservedID 391 } 392 393 // SetTransactionID does what it says 394 func (client *QueryClient) SetTransactionID(id int64) { 395 client.transactionID = id 396 } 397 398 // SetReservedID does what it says 399 func (client *QueryClient) SetReservedID(id int64) { 400 client.reservedID = id 401 } 402 403 // StreamHealth receives the health response 404 func (client *QueryClient) StreamHealth(sendFunc func(*querypb.StreamHealthResponse) error) error { 405 return client.server.StreamHealth(client.ctx, sendFunc) 406 } 407 408 func (client *QueryClient) UpdateContext(ctx context.Context) { 409 client.ctx = ctx 410 } 411 412 func (client *QueryClient) GetSchema(tableType querypb.SchemaTableType, tableNames ...string) (map[string]string, error) { 413 schemaDef := map[string]string{} 414 err := client.server.GetSchema(client.ctx, client.target, tableType, tableNames, func(schemaRes *querypb.GetSchemaResponse) error { 415 schemaDef = schemaRes.TableDefinition 416 return nil 417 }) 418 if err != nil { 419 return nil, err 420 } 421 return schemaDef, nil 422 }