vitess.io/vitess@v0.16.2/go/vt/vitessdriver/fakeserver_test.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 vitessdriver
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"reflect"
    24  
    25  	"google.golang.org/protobuf/proto"
    26  
    27  	"vitess.io/vitess/go/sqltypes"
    28  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    29  	querypb "vitess.io/vitess/go/vt/proto/query"
    30  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    31  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    32  	"vitess.io/vitess/go/vt/vtgate/vtgateservice"
    33  )
    34  
    35  // fakeVTGateService has the server side of this fake
    36  type fakeVTGateService struct {
    37  }
    38  
    39  // queryExecute contains all the fields we use to test Execute
    40  type queryExecute struct {
    41  	SQL           string
    42  	BindVariables map[string]*querypb.BindVariable
    43  	Session       *vtgatepb.Session
    44  }
    45  
    46  func (q *queryExecute) Equal(q2 *queryExecute) bool {
    47  	return q.SQL == q2.SQL &&
    48  		reflect.DeepEqual(q.BindVariables, q2.BindVariables) &&
    49  		proto.Equal(q.Session, q2.Session)
    50  }
    51  
    52  // Execute is part of the VTGateService interface
    53  func (f *fakeVTGateService) Execute(ctx context.Context, session *vtgatepb.Session, sql string, bindVariables map[string]*querypb.BindVariable) (*vtgatepb.Session, *sqltypes.Result, error) {
    54  	execCase, ok := execMap[sql]
    55  	if !ok {
    56  		return session, nil, fmt.Errorf("no match for: %s", sql)
    57  	}
    58  	query := &queryExecute{
    59  		SQL:           sql,
    60  		BindVariables: bindVariables,
    61  		Session:       session,
    62  	}
    63  	if !query.Equal(execCase.execQuery) {
    64  		return session, nil, fmt.Errorf("Execute request mismatch: got %+v, want %+v", query, execCase.execQuery)
    65  	}
    66  	if execCase.session != nil {
    67  		proto.Reset(session)
    68  		proto.Merge(session, execCase.session)
    69  	}
    70  	return session, execCase.result, nil
    71  }
    72  
    73  // ExecuteBatch is part of the VTGateService interface
    74  func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, session *vtgatepb.Session, sql []string, bindVariables []map[string]*querypb.BindVariable) (*vtgatepb.Session, []sqltypes.QueryResponse, error) {
    75  	if len(sql) == 1 {
    76  		execCase, ok := execMap[sql[0]]
    77  		if !ok {
    78  			return session, nil, fmt.Errorf("no match for: %s", sql)
    79  		}
    80  		if bindVariables == nil {
    81  			bindVariables = make([]map[string]*querypb.BindVariable, 1)
    82  		}
    83  		query := &queryExecute{
    84  			SQL:           sql[0],
    85  			BindVariables: bindVariables[0],
    86  			Session:       session,
    87  		}
    88  		if !query.Equal(execCase.execQuery) {
    89  			return session, nil, fmt.Errorf("Execute request mismatch: got %+v, want %+v", query, execCase.execQuery)
    90  		}
    91  		if execCase.session != nil {
    92  			proto.Reset(session)
    93  			proto.Merge(session, execCase.session)
    94  		}
    95  		return session, []sqltypes.QueryResponse{
    96  			{QueryResult: execCase.result},
    97  		}, nil
    98  	}
    99  	return session, nil, nil
   100  }
   101  
   102  // StreamExecute is part of the VTGateService interface
   103  func (f *fakeVTGateService) StreamExecute(ctx context.Context, session *vtgatepb.Session, sql string, bindVariables map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error) error {
   104  	execCase, ok := execMap[sql]
   105  	if !ok {
   106  		return fmt.Errorf("no match for: %s", sql)
   107  	}
   108  	query := &queryExecute{
   109  		SQL:           sql,
   110  		BindVariables: bindVariables,
   111  		Session:       session,
   112  	}
   113  	if !query.Equal(execCase.execQuery) {
   114  		return fmt.Errorf("request mismatch: got %+v, want %+v", query, execCase.execQuery)
   115  	}
   116  	if execCase.result != nil {
   117  		result := &sqltypes.Result{
   118  			Fields: execCase.result.Fields,
   119  		}
   120  		if err := callback(result); err != nil {
   121  			return err
   122  		}
   123  		for _, row := range execCase.result.Rows {
   124  			result := &sqltypes.Result{
   125  				Rows: [][]sqltypes.Value{row},
   126  			}
   127  			if err := callback(result); err != nil {
   128  				return err
   129  			}
   130  		}
   131  	}
   132  	return nil
   133  }
   134  
   135  // Prepare is part of the VTGateService interface
   136  func (f *fakeVTGateService) Prepare(ctx context.Context, session *vtgatepb.Session, sql string, bindVariables map[string]*querypb.BindVariable) (*vtgatepb.Session, []*querypb.Field, error) {
   137  	execCase, ok := execMap[sql]
   138  	if !ok {
   139  		return session, nil, fmt.Errorf("no match for: %s", sql)
   140  	}
   141  	query := &queryExecute{
   142  		SQL:           sql,
   143  		BindVariables: bindVariables,
   144  		Session:       session,
   145  	}
   146  	if !query.Equal(execCase.execQuery) {
   147  		return session, nil, fmt.Errorf("Prepare request mismatch: got %+v, want %+v", query, execCase.execQuery)
   148  	}
   149  	if execCase.session != nil {
   150  		proto.Reset(session)
   151  		proto.Merge(session, execCase.session)
   152  	}
   153  	return session, execCase.result.Fields, nil
   154  }
   155  
   156  func (f *fakeVTGateService) CloseSession(ctx context.Context, session *vtgatepb.Session) error {
   157  	return nil
   158  }
   159  
   160  // ResolveTransaction is part of the VTGateService interface
   161  func (f *fakeVTGateService) ResolveTransaction(ctx context.Context, dtid string) error {
   162  	if dtid != dtid2 {
   163  		return errors.New("ResolveTransaction: dtid mismatch")
   164  	}
   165  	return nil
   166  }
   167  
   168  func (f *fakeVTGateService) VStream(ctx context.Context, tabletType topodatapb.TabletType, vgtid *binlogdatapb.VGtid, filter *binlogdatapb.Filter, flags *vtgatepb.VStreamFlags, send func([]*binlogdatapb.VEvent) error) error {
   169  	return nil
   170  }
   171  
   172  // HandlePanic is part of the VTGateService interface
   173  func (f *fakeVTGateService) HandlePanic(err *error) {
   174  	if x := recover(); x != nil {
   175  		*err = fmt.Errorf("uncaught panic: %v", x)
   176  	}
   177  }
   178  
   179  // CreateFakeServer returns the fake server for the tests
   180  func CreateFakeServer() vtgateservice.VTGateService {
   181  	return &fakeVTGateService{}
   182  }
   183  
   184  var execMap = map[string]struct {
   185  	execQuery *queryExecute
   186  	result    *sqltypes.Result
   187  	session   *vtgatepb.Session
   188  	err       error
   189  }{
   190  	"request": {
   191  		execQuery: &queryExecute{
   192  			SQL: "request",
   193  			BindVariables: map[string]*querypb.BindVariable{
   194  				"v1": sqltypes.Int64BindVariable(0),
   195  			},
   196  			Session: &vtgatepb.Session{
   197  				TargetString: "@rdonly",
   198  				Autocommit:   true,
   199  			},
   200  		},
   201  		result:  &result1,
   202  		session: nil,
   203  	},
   204  	"requestDates": {
   205  		execQuery: &queryExecute{
   206  			SQL: "requestDates",
   207  			BindVariables: map[string]*querypb.BindVariable{
   208  				"v1": sqltypes.Int64BindVariable(0),
   209  			},
   210  			Session: &vtgatepb.Session{
   211  				TargetString: "@rdonly",
   212  				Autocommit:   true,
   213  			},
   214  		},
   215  		result:  &result2,
   216  		session: nil,
   217  	},
   218  	"txRequest": {
   219  		execQuery: &queryExecute{
   220  			SQL: "txRequest",
   221  			BindVariables: map[string]*querypb.BindVariable{
   222  				"v1": sqltypes.Int64BindVariable(0),
   223  			},
   224  			Session: session1,
   225  		},
   226  		result:  &sqltypes.Result{},
   227  		session: session2,
   228  	},
   229  	"distributedTxRequest": {
   230  		execQuery: &queryExecute{
   231  			SQL: "distributedTxRequest",
   232  			BindVariables: map[string]*querypb.BindVariable{
   233  				"v1": sqltypes.Int64BindVariable(1),
   234  			},
   235  			Session: &vtgatepb.Session{
   236  				InTransaction: true,
   237  				ShardSessions: []*vtgatepb.Session_ShardSession{
   238  					{
   239  						Target: &querypb.Target{
   240  							Keyspace:   "ks",
   241  							Shard:      "1",
   242  							TabletType: topodatapb.TabletType_PRIMARY,
   243  						},
   244  						TransactionId: 1,
   245  					},
   246  				},
   247  				TargetString: "@rdonly",
   248  			},
   249  		},
   250  		result:  &sqltypes.Result{},
   251  		session: session2,
   252  	},
   253  	"begin": {
   254  		execQuery: &queryExecute{
   255  			SQL: "begin",
   256  			Session: &vtgatepb.Session{
   257  				TargetString: "@primary",
   258  				Autocommit:   true,
   259  			},
   260  		},
   261  		result:  &sqltypes.Result{},
   262  		session: session1,
   263  	},
   264  	"commit": {
   265  		execQuery: &queryExecute{
   266  			SQL:     "commit",
   267  			Session: session2,
   268  		},
   269  		result: &sqltypes.Result{},
   270  		session: &vtgatepb.Session{
   271  			TargetString: "@primary",
   272  			Autocommit:   true,
   273  		},
   274  	},
   275  	"rollback": {
   276  		execQuery: &queryExecute{
   277  			SQL:     "rollback",
   278  			Session: session2,
   279  		},
   280  		result: &sqltypes.Result{},
   281  		session: &vtgatepb.Session{
   282  			TargetString: "@primary",
   283  		},
   284  	},
   285  }
   286  
   287  var result1 = sqltypes.Result{
   288  	Fields: []*querypb.Field{
   289  		{
   290  			Name: "field1",
   291  			Type: sqltypes.Int16,
   292  		},
   293  		{
   294  			Name: "field2",
   295  			Type: sqltypes.VarChar,
   296  		},
   297  	},
   298  	RowsAffected: 123,
   299  	InsertID:     72,
   300  	Rows: [][]sqltypes.Value{
   301  		{
   302  			sqltypes.NewVarBinary("1"),
   303  			sqltypes.NewVarBinary("value1"),
   304  		},
   305  		{
   306  			sqltypes.NewVarBinary("2"),
   307  			sqltypes.NewVarBinary("value2"),
   308  		},
   309  	},
   310  }
   311  
   312  var result2 = sqltypes.Result{
   313  	Fields: []*querypb.Field{
   314  		{
   315  			Name: "fieldDatetime",
   316  			Type: sqltypes.Datetime,
   317  		},
   318  		{
   319  			Name: "fieldDate",
   320  			Type: sqltypes.Date,
   321  		},
   322  	},
   323  	RowsAffected: 42,
   324  	InsertID:     73,
   325  	Rows: [][]sqltypes.Value{
   326  		{
   327  			sqltypes.NewVarBinary("2009-03-29 17:22:11"),
   328  			sqltypes.NewVarBinary("2006-07-02"),
   329  		},
   330  		{
   331  			sqltypes.NewVarBinary("0000-00-00 00:00:00"),
   332  			sqltypes.NewVarBinary("0000-00-00"),
   333  		},
   334  	},
   335  }
   336  
   337  var session1 = &vtgatepb.Session{
   338  	InTransaction: true,
   339  	TargetString:  "@rdonly",
   340  }
   341  
   342  var session2 = &vtgatepb.Session{
   343  	InTransaction: true,
   344  	ShardSessions: []*vtgatepb.Session_ShardSession{
   345  		{
   346  			Target: &querypb.Target{
   347  				Keyspace:   "ks",
   348  				Shard:      "1",
   349  				TabletType: topodatapb.TabletType_PRIMARY,
   350  			},
   351  			TransactionId: 1,
   352  		},
   353  	},
   354  	TargetString: "@rdonly",
   355  }
   356  
   357  var dtid2 = "aa"