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  }