vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletconntest/fakequeryservice.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 tabletconntest
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"testing"
    24  
    25  	"vitess.io/vitess/go/vt/vttablet/queryservice"
    26  
    27  	"google.golang.org/protobuf/proto"
    28  
    29  	"vitess.io/vitess/go/sqltypes"
    30  	"vitess.io/vitess/go/vt/callerid"
    31  
    32  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    33  	querypb "vitess.io/vitess/go/vt/proto/query"
    34  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    35  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    36  )
    37  
    38  // FakeQueryService implements a programmable fake for the query service
    39  // server side.
    40  type FakeQueryService struct {
    41  	t              testing.TB
    42  	TestingGateway bool
    43  
    44  	// these fields are used to simulate and synchronize on errors
    45  	HasError        bool
    46  	HasBeginError   bool
    47  	HasReserveError bool
    48  	TabletError     error
    49  	ErrorWait       chan struct{}
    50  
    51  	// these fields are used to simulate and synchronize on panics
    52  	Panics                   bool
    53  	StreamExecutePanicsEarly bool
    54  	PanicWait                chan struct{}
    55  
    56  	// ExpectedTransactionID is what transactionID to expect for Execute
    57  	ExpectedTransactionID int64
    58  
    59  	// StreamHealthResponse is what we return for StreamHealth.
    60  	// If not set, return TestStreamHealthStreamHealthResponse
    61  	StreamHealthResponse *querypb.StreamHealthResponse
    62  }
    63  
    64  var _ queryservice.QueryService = (*FakeQueryService)(nil)
    65  
    66  // Close is a no-op.
    67  func (f *FakeQueryService) Close(ctx context.Context) error {
    68  	return nil
    69  }
    70  
    71  // HandlePanic is part of the queryservice.QueryService interface
    72  func (f *FakeQueryService) HandlePanic(err *error) {
    73  	if x := recover(); x != nil {
    74  		*err = fmt.Errorf("caught test panic: %v", x)
    75  	}
    76  }
    77  
    78  // TestTarget is the target we use for this test
    79  var TestTarget = &querypb.Target{
    80  	Keyspace:   "test_keyspace",
    81  	Shard:      "test_shard",
    82  	TabletType: topodatapb.TabletType_REPLICA,
    83  }
    84  
    85  // TestCell is the cell we use for this test (and TestGRPCDiscovery)
    86  var TestCell = "aa"
    87  
    88  // TestAlias is the tablet alias we use for this test (and TestGRPCDiscovery)
    89  var TestAlias = &topodatapb.TabletAlias{
    90  	Cell: TestCell,
    91  	Uid:  1,
    92  }
    93  
    94  // TestCallerID is a test caller id.
    95  var TestCallerID = &vtrpcpb.CallerID{
    96  	Principal:    "test_principal",
    97  	Component:    "test_component",
    98  	Subcomponent: "test_subcomponent",
    99  }
   100  
   101  // TestVTGateCallerID is a test vtgate caller id.
   102  var TestVTGateCallerID = &querypb.VTGateCallerID{
   103  	Username: "test_username",
   104  }
   105  
   106  // TestExecuteOptions is a test execute options.
   107  var TestExecuteOptions = &querypb.ExecuteOptions{
   108  	IncludedFields:  querypb.ExecuteOptions_TYPE_ONLY,
   109  	ClientFoundRows: true,
   110  }
   111  
   112  // TestAsTransaction is a test 'asTransaction' flag.
   113  const TestAsTransaction bool = true
   114  
   115  func (f *FakeQueryService) checkTargetCallerID(ctx context.Context, name string, target *querypb.Target) {
   116  	if !proto.Equal(target, TestTarget) {
   117  		f.t.Errorf("invalid Target for %v: got %#v expected %#v", name, target, TestTarget)
   118  	}
   119  
   120  	ef := callerid.EffectiveCallerIDFromContext(ctx)
   121  	if ef == nil {
   122  		f.t.Errorf("no effective caller id for %v", name)
   123  	} else {
   124  		if !proto.Equal(ef, TestCallerID) {
   125  			f.t.Errorf("invalid effective caller id for %v: got %v expected %v", name, ef, TestCallerID)
   126  		}
   127  	}
   128  
   129  	im := callerid.ImmediateCallerIDFromContext(ctx)
   130  	if im == nil {
   131  		f.t.Errorf("no immediate caller id for %v", name)
   132  	} else {
   133  		if !proto.Equal(im, TestVTGateCallerID) {
   134  			f.t.Errorf("invalid immediate caller id for %v: got %v expected %v", name, im, TestVTGateCallerID)
   135  		}
   136  	}
   137  }
   138  
   139  // beginTransactionID is a test transaction id for Begin.
   140  const beginTransactionID int64 = 9990
   141  
   142  // Begin is part of the queryservice.QueryService interface
   143  func (f *FakeQueryService) Begin(ctx context.Context, target *querypb.Target, options *querypb.ExecuteOptions) (queryservice.TransactionState, error) {
   144  	if f.HasBeginError {
   145  		return queryservice.TransactionState{}, f.TabletError
   146  	}
   147  	if f.Panics {
   148  		panic(fmt.Errorf("test-triggered panic"))
   149  	}
   150  	f.checkTargetCallerID(ctx, "Begin", target)
   151  	if !proto.Equal(options, TestExecuteOptions) {
   152  		f.t.Errorf("invalid Execute.ExecuteOptions: got %v expected %v", options, TestExecuteOptions)
   153  	}
   154  	return queryservice.TransactionState{TransactionID: beginTransactionID, TabletAlias: TestAlias}, nil
   155  }
   156  
   157  // commitTransactionID is a test transaction id for Commit.
   158  const commitTransactionID int64 = 999044
   159  
   160  // Commit is part of the queryservice.QueryService interface
   161  func (f *FakeQueryService) Commit(ctx context.Context, target *querypb.Target, transactionID int64) (int64, error) {
   162  	if f.HasError {
   163  		return 0, f.TabletError
   164  	}
   165  	if f.Panics {
   166  		panic(fmt.Errorf("test-triggered panic"))
   167  	}
   168  	f.checkTargetCallerID(ctx, "Commit", target)
   169  	if transactionID != commitTransactionID {
   170  		f.t.Errorf("Commit: invalid TransactionId: got %v expected %v", transactionID, commitTransactionID)
   171  	}
   172  	return 0, nil
   173  }
   174  
   175  // rollbackTransactionID is a test transactin id for Rollback.
   176  const rollbackTransactionID int64 = 999044
   177  
   178  // Rollback is part of the queryservice.QueryService interface
   179  func (f *FakeQueryService) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) (int64, error) {
   180  	if f.HasError {
   181  		return 0, f.TabletError
   182  	}
   183  	if f.Panics {
   184  		panic(fmt.Errorf("test-triggered panic"))
   185  	}
   186  	f.checkTargetCallerID(ctx, "Rollback", target)
   187  	if transactionID != rollbackTransactionID {
   188  		f.t.Errorf("Rollback: invalid TransactionId: got %v expected %v", transactionID, rollbackTransactionID)
   189  	}
   190  	return 0, nil
   191  }
   192  
   193  // Dtid is a test dtid
   194  const Dtid string = "aa"
   195  
   196  // Prepare is part of the queryservice.QueryService interface
   197  func (f *FakeQueryService) Prepare(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) (err error) {
   198  	if f.HasError {
   199  		return f.TabletError
   200  	}
   201  	if f.Panics {
   202  		panic(fmt.Errorf("test-triggered panic"))
   203  	}
   204  	f.checkTargetCallerID(ctx, "Prepare", target)
   205  	if transactionID != commitTransactionID {
   206  		f.t.Errorf("Prepare: invalid TransactionID: got %v expected %v", transactionID, commitTransactionID)
   207  	}
   208  	if dtid != Dtid {
   209  		f.t.Errorf("Prepare: invalid dtid: got %s expected %s", dtid, Dtid)
   210  	}
   211  	return nil
   212  }
   213  
   214  // CommitPrepared is part of the queryservice.QueryService interface
   215  func (f *FakeQueryService) CommitPrepared(ctx context.Context, target *querypb.Target, dtid string) (err error) {
   216  	if f.HasError {
   217  		return f.TabletError
   218  	}
   219  	if f.Panics {
   220  		panic(fmt.Errorf("test-triggered panic"))
   221  	}
   222  	f.checkTargetCallerID(ctx, "CommitPrepared", target)
   223  	if dtid != Dtid {
   224  		f.t.Errorf("CommitPrepared: invalid dtid: got %s expected %s", dtid, Dtid)
   225  	}
   226  	return nil
   227  }
   228  
   229  // RollbackPrepared is part of the queryservice.QueryService interface
   230  func (f *FakeQueryService) RollbackPrepared(ctx context.Context, target *querypb.Target, dtid string, originalID int64) (err error) {
   231  	if f.HasError {
   232  		return f.TabletError
   233  	}
   234  	if f.Panics {
   235  		panic(fmt.Errorf("test-triggered panic"))
   236  	}
   237  	f.checkTargetCallerID(ctx, "RollbackPrepared", target)
   238  	if originalID != rollbackTransactionID {
   239  		f.t.Errorf("RollbackPrepared: invalid TransactionID: got %v expected %v", originalID, rollbackTransactionID)
   240  	}
   241  	if dtid != Dtid {
   242  		f.t.Errorf("RollbackPrepared: invalid dtid: got %s expected %s", dtid, Dtid)
   243  	}
   244  	return nil
   245  }
   246  
   247  // Participants is a test list of 2pc participants.
   248  var Participants = []*querypb.Target{{
   249  	Keyspace: "ks0",
   250  	Shard:    "0",
   251  }, {
   252  	Keyspace: "ks1",
   253  	Shard:    "1",
   254  }}
   255  
   256  // TargetsEqual returns true if the targets are equal.
   257  func TargetsEqual(t1, t2 []*querypb.Target) bool {
   258  	if len(t1) != len(t2) {
   259  		return false
   260  	}
   261  	for i, t := range t1 {
   262  		if !proto.Equal(t, t2[i]) {
   263  			return false
   264  		}
   265  	}
   266  	return true
   267  }
   268  
   269  // CreateTransaction is part of the queryservice.QueryService interface
   270  func (f *FakeQueryService) CreateTransaction(ctx context.Context, target *querypb.Target, dtid string, participants []*querypb.Target) (err error) {
   271  	if f.HasError {
   272  		return f.TabletError
   273  	}
   274  	if f.Panics {
   275  		panic(fmt.Errorf("test-triggered panic"))
   276  	}
   277  	f.checkTargetCallerID(ctx, "CreateTransaction", target)
   278  	if dtid != Dtid {
   279  		f.t.Errorf("CreateTransaction: invalid dtid: got %s expected %s", dtid, Dtid)
   280  	}
   281  	if !TargetsEqual(participants, Participants) {
   282  		f.t.Errorf("invalid CreateTransaction participants: got %v, expected %v", participants, Participants)
   283  	}
   284  	return nil
   285  }
   286  
   287  // StartCommit is part of the queryservice.QueryService interface
   288  func (f *FakeQueryService) StartCommit(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) (err error) {
   289  	if f.HasError {
   290  		return f.TabletError
   291  	}
   292  	if f.Panics {
   293  		panic(fmt.Errorf("test-triggered panic"))
   294  	}
   295  	f.checkTargetCallerID(ctx, "StartCommit", target)
   296  	if transactionID != commitTransactionID {
   297  		f.t.Errorf("StartCommit: invalid TransactionID: got %v expected %v", transactionID, commitTransactionID)
   298  	}
   299  	if dtid != Dtid {
   300  		f.t.Errorf("StartCommit: invalid dtid: got %s expected %s", dtid, Dtid)
   301  	}
   302  	return nil
   303  }
   304  
   305  // SetRollback is part of the queryservice.QueryService interface
   306  func (f *FakeQueryService) SetRollback(ctx context.Context, target *querypb.Target, dtid string, transactionID int64) (err error) {
   307  	if f.HasError {
   308  		return f.TabletError
   309  	}
   310  	if f.Panics {
   311  		panic(fmt.Errorf("test-triggered panic"))
   312  	}
   313  	f.checkTargetCallerID(ctx, "SetRollback", target)
   314  	if transactionID != commitTransactionID {
   315  		f.t.Errorf("SetRollback: invalid TransactionID: got %v expected %v", transactionID, commitTransactionID)
   316  	}
   317  	if dtid != Dtid {
   318  		f.t.Errorf("SetRollback: invalid dtid: got %s expected %s", dtid, Dtid)
   319  	}
   320  	return nil
   321  }
   322  
   323  // ConcludeTransaction is part of the queryservice.QueryService interface
   324  func (f *FakeQueryService) ConcludeTransaction(ctx context.Context, target *querypb.Target, dtid string) (err error) {
   325  	if f.HasError {
   326  		return f.TabletError
   327  	}
   328  	if f.Panics {
   329  		panic(fmt.Errorf("test-triggered panic"))
   330  	}
   331  	f.checkTargetCallerID(ctx, "ConcludeTransaction", target)
   332  	if dtid != Dtid {
   333  		f.t.Errorf("ConcludeTransaction: invalid dtid: got %s expected %s", dtid, Dtid)
   334  	}
   335  	return nil
   336  }
   337  
   338  // Metadata is a test metadata for 2pc transactions.
   339  var Metadata = &querypb.TransactionMetadata{
   340  	Dtid:         "aa",
   341  	State:        querypb.TransactionState_PREPARE,
   342  	TimeCreated:  1,
   343  	Participants: Participants,
   344  }
   345  
   346  // ReadTransaction is part of the queryservice.QueryService interface
   347  func (f *FakeQueryService) ReadTransaction(ctx context.Context, target *querypb.Target, dtid string) (metadata *querypb.TransactionMetadata, err error) {
   348  	if f.HasError {
   349  		return nil, f.TabletError
   350  	}
   351  	if f.Panics {
   352  		panic(fmt.Errorf("test-triggered panic"))
   353  	}
   354  	f.checkTargetCallerID(ctx, "ReadTransaction", target)
   355  	if dtid != Dtid {
   356  		f.t.Errorf("ReadTransaction: invalid dtid: got %s expected %s", dtid, Dtid)
   357  	}
   358  	return Metadata, nil
   359  }
   360  
   361  // ExecuteQuery is a fake test query.
   362  const ExecuteQuery = "executeQuery"
   363  
   364  // ExecuteBindVars is a test bind var.
   365  var ExecuteBindVars = map[string]*querypb.BindVariable{
   366  	"bind1": sqltypes.Int64BindVariable(1114444),
   367  }
   368  
   369  // ExecuteTransactionID is a test transaction id.
   370  const ExecuteTransactionID int64 = 678
   371  
   372  // ReserveConnectionID is a test reserved connection id.
   373  const ReserveConnectionID int64 = 933
   374  
   375  // ExecuteQueryResult is a test query result.
   376  var ExecuteQueryResult = sqltypes.Result{
   377  	Fields: []*querypb.Field{
   378  		{
   379  			Name: "field1",
   380  			Type: sqltypes.Int8,
   381  		},
   382  		{
   383  			Name: "field2",
   384  			Type: sqltypes.Char,
   385  		},
   386  	},
   387  	RowsAffected: 123,
   388  	InsertID:     72,
   389  	Rows: [][]sqltypes.Value{
   390  		{
   391  			sqltypes.TestValue(sqltypes.Int8, "1"),
   392  			sqltypes.NULL,
   393  		},
   394  		{
   395  			sqltypes.TestValue(sqltypes.Int8, "2"),
   396  			sqltypes.TestValue(sqltypes.Char, "row2 value2"),
   397  		},
   398  	},
   399  }
   400  
   401  // Execute is part of the queryservice.QueryService interface
   402  func (f *FakeQueryService) Execute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID, reservedID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, error) {
   403  	if f.HasError {
   404  		return nil, f.TabletError
   405  	}
   406  	if f.Panics {
   407  		panic(fmt.Errorf("test-triggered panic"))
   408  	}
   409  	if sql != ExecuteQuery {
   410  		f.t.Errorf("invalid Execute.Query.Sql: got %v expected %v", sql, ExecuteQuery)
   411  	}
   412  	if !sqltypes.BindVariablesEqual(bindVariables, ExecuteBindVars) {
   413  		f.t.Errorf("invalid Execute.BindVariables: got %v expected %v", bindVariables, ExecuteBindVars)
   414  	}
   415  	if !proto.Equal(options, TestExecuteOptions) {
   416  		f.t.Errorf("invalid Execute.ExecuteOptions: got %v expected %v", options, TestExecuteOptions)
   417  	}
   418  	f.checkTargetCallerID(ctx, "Execute", target)
   419  	if transactionID != f.ExpectedTransactionID {
   420  		f.t.Errorf("invalid Execute.TransactionId: got %v expected %v", transactionID, f.ExpectedTransactionID)
   421  	}
   422  	return &ExecuteQueryResult, nil
   423  }
   424  
   425  // StreamExecuteQuery is a fake test query for streaming.
   426  const StreamExecuteQuery = "streamExecuteQuery"
   427  
   428  // StreamExecuteBindVars is a test bind var for streaming.
   429  var StreamExecuteBindVars = map[string]*querypb.BindVariable{
   430  	"bind1": sqltypes.Int64BindVariable(93848000),
   431  }
   432  
   433  // StreamExecuteQueryResult1 is the first packet of a streaming result.
   434  var StreamExecuteQueryResult1 = sqltypes.Result{
   435  	Fields: []*querypb.Field{
   436  		{
   437  			Name: "field1",
   438  			Type: sqltypes.Int8,
   439  		},
   440  		{
   441  			Name: "field2",
   442  			Type: sqltypes.Char,
   443  		},
   444  	},
   445  }
   446  
   447  // StreamExecuteQueryResult2 is the second packet of a streaming result.
   448  var StreamExecuteQueryResult2 = sqltypes.Result{
   449  	Rows: [][]sqltypes.Value{
   450  		{
   451  			sqltypes.TestValue(sqltypes.Int8, "1"),
   452  			sqltypes.TestValue(sqltypes.Char, "row1 value2"),
   453  		},
   454  		{
   455  			sqltypes.TestValue(sqltypes.Int8, "2"),
   456  			sqltypes.TestValue(sqltypes.Char, "row2 value2"),
   457  		},
   458  	},
   459  }
   460  
   461  // StreamExecute is part of the queryservice.QueryService interface
   462  func (f *FakeQueryService) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error {
   463  	if f.Panics && f.StreamExecutePanicsEarly {
   464  		panic(fmt.Errorf("test-triggered panic early"))
   465  	}
   466  	if sql != StreamExecuteQuery {
   467  		f.t.Errorf("invalid StreamExecute.Sql: got %v expected %v", sql, StreamExecuteQuery)
   468  	}
   469  	if !sqltypes.BindVariablesEqual(bindVariables, StreamExecuteBindVars) {
   470  		f.t.Errorf("invalid StreamExecute.BindVariables: got %v expected %v", bindVariables, StreamExecuteBindVars)
   471  	}
   472  	if !proto.Equal(options, TestExecuteOptions) {
   473  		f.t.Errorf("invalid StreamExecute.ExecuteOptions: got %v expected %v", options, TestExecuteOptions)
   474  	}
   475  	f.checkTargetCallerID(ctx, "StreamExecute", target)
   476  	if err := callback(&StreamExecuteQueryResult1); err != nil {
   477  		f.t.Errorf("callback1 failed: %v", err)
   478  	}
   479  	if f.Panics && !f.StreamExecutePanicsEarly {
   480  		// wait until the client gets the response, then panics
   481  		<-f.PanicWait
   482  		panic(fmt.Errorf("test-triggered panic late"))
   483  	}
   484  	if f.HasError {
   485  		// wait until the client has the response, since all
   486  		// streaming implementation may not send previous
   487  		// messages if an error has been triggered.
   488  		<-f.ErrorWait
   489  		return f.TabletError
   490  	}
   491  	if err := callback(&StreamExecuteQueryResult2); err != nil {
   492  		f.t.Errorf("callback2 failed: %v", err)
   493  	}
   494  	return nil
   495  }
   496  
   497  // ExecuteBatchQueries are test queries for batch.
   498  var ExecuteBatchQueries = []*querypb.BoundQuery{
   499  	{
   500  		Sql: "executeBatchQueries1",
   501  		BindVariables: map[string]*querypb.BindVariable{
   502  			"bind1": sqltypes.Int64BindVariable(43),
   503  		},
   504  	},
   505  	{
   506  		Sql: "executeBatchQueries2",
   507  		BindVariables: map[string]*querypb.BindVariable{
   508  			"bind2": sqltypes.Int64BindVariable(72),
   509  		},
   510  	},
   511  }
   512  
   513  // ExecuteBatchTransactionID is a test transaction id for batch.
   514  const ExecuteBatchTransactionID int64 = 678
   515  
   516  // ExecuteBatchQueryResultList is a list of test query results.
   517  var ExecuteBatchQueryResultList = []sqltypes.Result{
   518  	{
   519  		Fields: []*querypb.Field{
   520  			{
   521  				Name: "field1",
   522  				Type: sqltypes.Int8,
   523  			},
   524  		},
   525  		RowsAffected: 1232,
   526  		InsertID:     712,
   527  		Rows: [][]sqltypes.Value{
   528  			{
   529  				sqltypes.TestValue(sqltypes.Int8, "1"),
   530  			},
   531  			{
   532  				sqltypes.TestValue(sqltypes.Int8, "2"),
   533  			},
   534  		},
   535  	},
   536  	{
   537  		Fields: []*querypb.Field{
   538  			{
   539  				Name: "field1",
   540  				Type: sqltypes.VarBinary,
   541  			},
   542  		},
   543  		RowsAffected: 12333,
   544  		InsertID:     74442,
   545  		Rows: [][]sqltypes.Value{
   546  			{
   547  				sqltypes.NewVarBinary("row1 value1"),
   548  			},
   549  			{
   550  				sqltypes.NewVarBinary("row1 value2"),
   551  			},
   552  		},
   553  	},
   554  }
   555  
   556  // BeginExecute combines Begin and Execute.
   557  func (f *FakeQueryService) BeginExecute(ctx context.Context, target *querypb.Target, _ []string, sql string, bindVariables map[string]*querypb.BindVariable, reservedID int64, options *querypb.ExecuteOptions) (queryservice.TransactionState, *sqltypes.Result, error) {
   558  	state, err := f.Begin(ctx, target, options)
   559  	if err != nil {
   560  		return state, nil, err
   561  	}
   562  
   563  	// TODO(deepthi): what alias should we actually return here?
   564  	result, err := f.Execute(ctx, target, sql, bindVariables, state.TransactionID, reservedID, options)
   565  	return state, result, err
   566  }
   567  
   568  // BeginStreamExecute combines Begin and StreamExecute.
   569  func (f *FakeQueryService) BeginStreamExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (queryservice.TransactionState, error) {
   570  	state, err := f.Begin(ctx, target, options)
   571  	if err != nil {
   572  		return state, err
   573  	}
   574  
   575  	for _, preQuery := range preQueries {
   576  		_, err := f.Execute(ctx, target, preQuery, nil, state.TransactionID, reservedID, options)
   577  		if err != nil {
   578  			return state, err
   579  		}
   580  	}
   581  
   582  	err = f.StreamExecute(ctx, target, sql, bindVariables, state.TransactionID, reservedID, options, callback)
   583  	return state, err
   584  }
   585  
   586  var (
   587  	// MessageName is a test message name.
   588  	MessageName = "vitess_message"
   589  
   590  	// MessageStreamResult is a test stream result.
   591  	MessageStreamResult = &sqltypes.Result{
   592  		Fields: []*querypb.Field{{
   593  			Name: "id",
   594  			Type: sqltypes.VarBinary,
   595  		}, {
   596  			Name: "message",
   597  			Type: sqltypes.VarBinary,
   598  		}},
   599  		Rows: [][]sqltypes.Value{{
   600  			sqltypes.NewVarBinary("1"),
   601  			sqltypes.NewVarBinary("row1 value2"),
   602  		}, {
   603  			sqltypes.NewVarBinary("2"),
   604  			sqltypes.NewVarBinary("row2 value2"),
   605  		}},
   606  	}
   607  
   608  	// MessageIDs is a test list of message ids.
   609  	MessageIDs = []*querypb.Value{{
   610  		Type:  sqltypes.VarChar,
   611  		Value: []byte("1"),
   612  	}}
   613  )
   614  
   615  // MessageStream is part of the queryservice.QueryService interface
   616  func (f *FakeQueryService) MessageStream(ctx context.Context, target *querypb.Target, name string, callback func(*sqltypes.Result) error) (err error) {
   617  	if f.HasError {
   618  		return f.TabletError
   619  	}
   620  	if f.Panics {
   621  		panic(fmt.Errorf("test-triggered panic"))
   622  	}
   623  	if name != MessageName {
   624  		f.t.Errorf("name: %s, want %s", name, MessageName)
   625  	}
   626  	if err := callback(MessageStreamResult); err != nil {
   627  		f.t.Logf("MessageStream callback failed: %v", err)
   628  	}
   629  	return nil
   630  }
   631  
   632  // MessageAck is part of the queryservice.QueryService interface
   633  func (f *FakeQueryService) MessageAck(ctx context.Context, target *querypb.Target, name string, ids []*querypb.Value) (count int64, err error) {
   634  	if f.HasError {
   635  		return 0, f.TabletError
   636  	}
   637  	if f.Panics {
   638  		panic(fmt.Errorf("test-triggered panic"))
   639  	}
   640  	if name != MessageName {
   641  		f.t.Errorf("name: %s, want %s", name, MessageName)
   642  	}
   643  	if !sqltypes.Proto3ValuesEqual(ids, MessageIDs) {
   644  		f.t.Errorf("ids: %v, want %v", ids, MessageIDs)
   645  	}
   646  	return 1, nil
   647  }
   648  
   649  // TestStreamHealthStreamHealthResponse is a test stream health response.
   650  var TestStreamHealthStreamHealthResponse = &querypb.StreamHealthResponse{
   651  	Target: &querypb.Target{
   652  		Keyspace:   "test_keyspace",
   653  		Shard:      "test_shard",
   654  		TabletType: topodatapb.TabletType_RDONLY,
   655  	},
   656  	Serving: true,
   657  
   658  	TabletExternallyReparentedTimestamp: 1234589,
   659  
   660  	RealtimeStats: &querypb.RealtimeStats{
   661  		CpuUsage:                      1.0,
   662  		HealthError:                   "random error",
   663  		ReplicationLagSeconds:         234,
   664  		BinlogPlayersCount:            1,
   665  		FilteredReplicationLagSeconds: 2,
   666  	},
   667  }
   668  
   669  // TestStreamHealthErrorMsg is a test error message for health streaming.
   670  var TestStreamHealthErrorMsg = "to trigger a server error"
   671  
   672  // StreamHealth is part of the queryservice.QueryService interface
   673  func (f *FakeQueryService) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error {
   674  	if f.HasError {
   675  		return errors.New(TestStreamHealthErrorMsg)
   676  	}
   677  	if f.Panics {
   678  		panic(fmt.Errorf("test-triggered panic"))
   679  	}
   680  	shr := f.StreamHealthResponse
   681  	if shr == nil {
   682  		shr = TestStreamHealthStreamHealthResponse
   683  	}
   684  	if err := callback(shr); err != nil {
   685  		f.t.Logf("StreamHealth callback failed: %v", err)
   686  	}
   687  	return nil
   688  }
   689  
   690  // VStream is part of the queryservice.QueryService interface
   691  func (f *FakeQueryService) VStream(ctx context.Context, request *binlogdatapb.VStreamRequest, send func([]*binlogdatapb.VEvent) error) error {
   692  	panic("not implemented")
   693  }
   694  
   695  // VStreamRows is part of the QueryService interface.
   696  func (f *FakeQueryService) VStreamRows(ctx context.Context, request *binlogdatapb.VStreamRowsRequest, send func(*binlogdatapb.VStreamRowsResponse) error) error {
   697  	panic("not implemented")
   698  }
   699  
   700  // VStreamResults is part of the QueryService interface.
   701  func (f *FakeQueryService) VStreamResults(ctx context.Context, target *querypb.Target, query string, send func(*binlogdatapb.VStreamResultsResponse) error) error {
   702  	panic("not implemented")
   703  }
   704  
   705  // QueryServiceByAlias satisfies the Gateway interface
   706  func (f *FakeQueryService) QueryServiceByAlias(_ *topodatapb.TabletAlias, _ *querypb.Target) (queryservice.QueryService, error) {
   707  	panic("not implemented")
   708  }
   709  
   710  // ReserveBeginExecute satisfies the Gateway interface
   711  func (f *FakeQueryService) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, postBeginQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (queryservice.ReservedTransactionState, *sqltypes.Result, error) {
   712  	panic("implement me")
   713  }
   714  
   715  // ReserveBeginStreamExecute satisfies the Gateway interface
   716  func (f *FakeQueryService) ReserveBeginStreamExecute(ctx context.Context, target *querypb.Target, preQueries []string, postBeginQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (queryservice.ReservedTransactionState, error) {
   717  	panic("implement me")
   718  }
   719  
   720  // ReserveExecute implements the QueryService interface
   721  func (f *FakeQueryService) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (queryservice.ReservedState, *sqltypes.Result, error) {
   722  	panic("implement me")
   723  }
   724  
   725  // ReserveStreamExecute satisfies the Gateway interface
   726  func (f *FakeQueryService) ReserveStreamExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (queryservice.ReservedState, error) {
   727  	state, err := f.reserve(transactionID)
   728  	if err != nil {
   729  		return state, err
   730  	}
   731  	err = f.StreamExecute(ctx, target, sql, bindVariables, transactionID, state.ReservedID, options, callback)
   732  	return state, err
   733  }
   734  
   735  func (f *FakeQueryService) reserve(transactionID int64) (queryservice.ReservedState, error) {
   736  	reserveID := transactionID
   737  	if reserveID == 0 {
   738  		reserveID = beginTransactionID
   739  	}
   740  	if f.HasReserveError {
   741  		return queryservice.ReservedState{}, f.TabletError
   742  	}
   743  	state := queryservice.ReservedState{ReservedID: reserveID, TabletAlias: TestAlias}
   744  	return state, nil
   745  }
   746  
   747  // Release implements the QueryService interface
   748  func (f *FakeQueryService) Release(ctx context.Context, target *querypb.Target, transactionID, reservedID int64) error {
   749  	panic("implement me")
   750  }
   751  
   752  // GetSchema implements the QueryService interface
   753  func (f *FakeQueryService) GetSchema(ctx context.Context, target *querypb.Target, tableType querypb.SchemaTableType, tableNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) error {
   754  	panic("implement me")
   755  }
   756  
   757  // CreateFakeServer returns the fake server for the tests
   758  func CreateFakeServer(t testing.TB) *FakeQueryService {
   759  	return &FakeQueryService{
   760  		t: t,
   761  	}
   762  }