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"