vitess.io/vitess@v0.16.2/go/vt/vtgate/executor_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 vtgate 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "fmt" 24 "html/template" 25 "net/http" 26 "net/http/httptest" 27 "reflect" 28 "sort" 29 "strings" 30 "testing" 31 32 "vitess.io/vitess/go/vt/vtgate/logstats" 33 34 "google.golang.org/protobuf/proto" 35 36 "vitess.io/vitess/go/cache" 37 "vitess.io/vitess/go/test/utils" 38 "vitess.io/vitess/go/vt/vtgate/engine" 39 40 "vitess.io/vitess/go/vt/topo" 41 42 "github.com/google/go-cmp/cmp" 43 44 "vitess.io/vitess/go/mysql" 45 "vitess.io/vitess/go/sqltypes" 46 "vitess.io/vitess/go/vt/callerid" 47 "vitess.io/vitess/go/vt/sqlparser" 48 "vitess.io/vitess/go/vt/vtgate/vindexes" 49 "vitess.io/vitess/go/vt/vtgate/vschemaacl" 50 51 querypb "vitess.io/vitess/go/vt/proto/query" 52 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 53 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 54 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 55 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 56 57 "github.com/stretchr/testify/assert" 58 "github.com/stretchr/testify/require" 59 ) 60 61 func TestExecutorResultsExceeded(t *testing.T) { 62 save := warnMemoryRows 63 warnMemoryRows = 3 64 defer func() { warnMemoryRows = save }() 65 66 executor, _, _, sbclookup := createExecutorEnv() 67 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) 68 69 initial := warnings.Counts()["ResultsExceeded"] 70 71 result1 := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1") 72 result2 := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1", "2", "3", "4") 73 sbclookup.SetResults([]*sqltypes.Result{result1, result2}) 74 75 _, err := executor.Execute(ctx, "TestExecutorResultsExceeded", session, "select * from main1", nil) 76 require.NoError(t, err) 77 assert.Equal(t, initial, warnings.Counts()["ResultsExceeded"], "warnings count") 78 79 _, err = executor.Execute(ctx, "TestExecutorResultsExceeded", session, "select * from main1", nil) 80 require.NoError(t, err) 81 assert.Equal(t, initial+1, warnings.Counts()["ResultsExceeded"], "warnings count") 82 } 83 84 func TestExecutorMaxMemoryRowsExceeded(t *testing.T) { 85 save := maxMemoryRows 86 maxMemoryRows = 3 87 defer func() { maxMemoryRows = save }() 88 89 executor, _, _, sbclookup := createExecutorEnv() 90 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) 91 result := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1", "2", "3", "4") 92 fn := func(r *sqltypes.Result) error { 93 return nil 94 } 95 testCases := []struct { 96 query string 97 err string 98 }{ 99 {"select /*vt+ IGNORE_MAX_MEMORY_ROWS=1 */ * from main1", ""}, 100 {"select * from main1", "in-memory row count exceeded allowed limit of 3"}, 101 } 102 103 for _, test := range testCases { 104 sbclookup.SetResults([]*sqltypes.Result{result}) 105 stmt, err := sqlparser.Parse(test.query) 106 require.NoError(t, err) 107 108 _, err = executor.Execute(ctx, "TestExecutorMaxMemoryRowsExceeded", session, test.query, nil) 109 if sqlparser.IgnoreMaxMaxMemoryRowsDirective(stmt) { 110 require.NoError(t, err, "no error when DirectiveIgnoreMaxMemoryRows is provided") 111 } else { 112 assert.EqualError(t, err, test.err, "maxMemoryRows limit exceeded") 113 } 114 115 sbclookup.SetResults([]*sqltypes.Result{result}) 116 err = executor.StreamExecute(ctx, "TestExecutorMaxMemoryRowsExceeded", session, test.query, nil, fn) 117 require.NoError(t, err, "maxMemoryRows limit does not apply to StreamExecute") 118 } 119 } 120 121 func TestExecutorTransactionsNoAutoCommit(t *testing.T) { 122 executor, _, _, sbclookup := createExecutorEnv() 123 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"}) 124 125 logChan := QueryLogger.Subscribe("Test") 126 defer QueryLogger.Unsubscribe(logChan) 127 128 // begin. 129 _, err := executor.Execute(ctx, "TestExecute", session, "begin", nil) 130 require.NoError(t, err) 131 wantSession := &vtgatepb.Session{InTransaction: true, TargetString: "@primary", SessionUUID: "suuid"} 132 utils.MustMatch(t, wantSession, session.Session, "session") 133 assert.EqualValues(t, 0, sbclookup.CommitCount.Get(), "commit count") 134 logStats := testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0) 135 assert.EqualValues(t, 0, logStats.CommitTime, "logstats: expected zero CommitTime") 136 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 137 138 // commit. 139 _, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil) 140 require.NoError(t, err) 141 logStats = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1) 142 assert.EqualValues(t, 0, logStats.CommitTime, "logstats: expected zero CommitTime") 143 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 144 145 _, err = executor.Execute(context.Background(), "TestExecute", session, "commit", nil) 146 if err != nil { 147 t.Fatal(err) 148 } 149 wantSession = &vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"} 150 if !proto.Equal(session.Session, wantSession) { 151 t.Errorf("begin: %v, want %v", session.Session, wantSession) 152 } 153 if commitCount := sbclookup.CommitCount.Get(); commitCount != 1 { 154 t.Errorf("want 1, got %d", commitCount) 155 } 156 logStats = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1) 157 if logStats.CommitTime == 0 { 158 t.Errorf("logstats: expected non-zero CommitTime") 159 } 160 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 161 162 // rollback. 163 _, err = executor.Execute(ctx, "TestExecute", session, "begin", nil) 164 require.NoError(t, err) 165 _, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil) 166 require.NoError(t, err) 167 _, err = executor.Execute(ctx, "TestExecute", session, "rollback", nil) 168 require.NoError(t, err) 169 wantSession = &vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"} 170 utils.MustMatch(t, wantSession, session.Session, "session") 171 assert.EqualValues(t, 1, sbclookup.RollbackCount.Get(), "rollback count") 172 _ = testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0) 173 _ = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1) 174 logStats = testQueryLog(t, logChan, "TestExecute", "ROLLBACK", "rollback", 1) 175 if logStats.CommitTime == 0 { 176 t.Errorf("logstats: expected non-zero CommitTime") 177 } 178 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 179 180 // CloseSession doesn't log anything 181 err = executor.CloseSession(ctx, session) 182 require.NoError(t, err) 183 logStats = getQueryLog(logChan) 184 if logStats != nil { 185 t.Errorf("logstats: expected no record for no-op rollback, got %v", logStats) 186 } 187 188 // Prevent use of non-primary if in_transaction is on. 189 session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary", InTransaction: true}) 190 _, err = executor.Execute(ctx, "TestExecute", session, "use @replica", nil) 191 require.EqualError(t, err, `can't execute the given command because you have an active transaction`) 192 } 193 194 func TestDirectTargetRewrites(t *testing.T) { 195 executor, _, _, sbclookup := createExecutorEnv() 196 executor.normalize = true 197 198 session := &vtgatepb.Session{ 199 TargetString: "TestUnsharded/0@primary", 200 Autocommit: true, 201 TransactionMode: vtgatepb.TransactionMode_MULTI, 202 } 203 sql := "select database()" 204 205 _, err := executor.Execute(ctx, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) 206 require.NoError(t, err) 207 assertQueries(t, sbclookup, []*querypb.BoundQuery{{ 208 Sql: "select :__vtdbname as `database()` from dual", 209 BindVariables: map[string]*querypb.BindVariable{"__vtdbname": sqltypes.StringBindVariable("TestUnsharded/0@primary")}, 210 }}) 211 } 212 213 func TestExecutorTransactionsAutoCommit(t *testing.T) { 214 executor, _, _, sbclookup := createExecutorEnv() 215 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}) 216 217 logChan := QueryLogger.Subscribe("Test") 218 defer QueryLogger.Unsubscribe(logChan) 219 220 // begin. 221 _, err := executor.Execute(ctx, "TestExecute", session, "begin", nil) 222 require.NoError(t, err) 223 wantSession := &vtgatepb.Session{InTransaction: true, TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"} 224 utils.MustMatch(t, wantSession, session.Session, "session") 225 if commitCount := sbclookup.CommitCount.Get(); commitCount != 0 { 226 t.Errorf("want 0, got %d", commitCount) 227 } 228 logStats := testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0) 229 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 230 231 // commit. 232 _, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil) 233 require.NoError(t, err) 234 _, err = executor.Execute(ctx, "TestExecute", session, "commit", nil) 235 require.NoError(t, err) 236 wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"} 237 utils.MustMatch(t, wantSession, session.Session, "session") 238 assert.EqualValues(t, 1, sbclookup.CommitCount.Get()) 239 240 logStats = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1) 241 assert.EqualValues(t, 0, logStats.CommitTime) 242 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 243 logStats = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1) 244 assert.NotEqual(t, 0, logStats.CommitTime) 245 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 246 247 // rollback. 248 _, err = executor.Execute(ctx, "TestExecute", session, "begin", nil) 249 require.NoError(t, err) 250 _, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil) 251 require.NoError(t, err) 252 _, err = executor.Execute(ctx, "TestExecute", session, "rollback", nil) 253 require.NoError(t, err) 254 wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"} 255 utils.MustMatch(t, wantSession, session.Session, "session") 256 if rollbackCount := sbclookup.RollbackCount.Get(); rollbackCount != 1 { 257 t.Errorf("want 1, got %d", rollbackCount) 258 } 259 _ = testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0) 260 _ = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1) 261 logStats = testQueryLog(t, logChan, "TestExecute", "ROLLBACK", "rollback", 1) 262 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 263 } 264 265 func TestExecutorTransactionsAutoCommitStreaming(t *testing.T) { 266 executor, _, _, sbclookup := createExecutorEnv() 267 oltpOptions := &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLTP} 268 session := NewSafeSession(&vtgatepb.Session{ 269 TargetString: "@primary", 270 Autocommit: true, 271 Options: oltpOptions, 272 SessionUUID: "suuid", 273 }) 274 275 logChan := QueryLogger.Subscribe("Test") 276 defer QueryLogger.Unsubscribe(logChan) 277 278 var results []*sqltypes.Result 279 280 // begin. 281 err := executor.StreamExecute(ctx, "TestExecute", session, "begin", nil, func(result *sqltypes.Result) error { 282 results = append(results, result) 283 return nil 284 }) 285 286 require.EqualValues(t, 1, len(results), "should get empty result from begin") 287 assert.Empty(t, results[0].Rows, "should get empty result from begin") 288 289 require.NoError(t, err) 290 wantSession := &vtgatepb.Session{ 291 InTransaction: true, 292 TargetString: "@primary", 293 Autocommit: true, 294 Options: oltpOptions, 295 SessionUUID: "suuid", 296 } 297 utils.MustMatch(t, wantSession, session.Session, "session") 298 assert.Zero(t, sbclookup.CommitCount.Get()) 299 logStats := testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0) 300 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 301 302 // commit. 303 _, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil) 304 require.NoError(t, err) 305 _, err = executor.Execute(ctx, "TestExecute", session, "commit", nil) 306 require.NoError(t, err) 307 wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, Options: oltpOptions, SessionUUID: "suuid"} 308 utils.MustMatch(t, wantSession, session.Session, "session") 309 assert.EqualValues(t, 1, sbclookup.CommitCount.Get()) 310 311 logStats = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1) 312 assert.EqualValues(t, 0, logStats.CommitTime) 313 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 314 logStats = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1) 315 assert.NotEqual(t, 0, logStats.CommitTime) 316 assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID") 317 318 // rollback. 319 _, err = executor.Execute(ctx, "TestExecute", session, "begin", nil) 320 require.NoError(t, err) 321 _, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil) 322 require.NoError(t, err) 323 _, err = executor.Execute(ctx, "TestExecute", session, "rollback", nil) 324 require.NoError(t, err) 325 wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, Options: oltpOptions, SessionUUID: "suuid"} 326 utils.MustMatch(t, wantSession, session.Session, "session") 327 assert.EqualValues(t, 1, sbclookup.RollbackCount.Get()) 328 } 329 330 func TestExecutorDeleteMetadata(t *testing.T) { 331 vschemaacl.AuthorizedDDLUsers = "%" 332 defer func() { 333 vschemaacl.AuthorizedDDLUsers = "" 334 }() 335 336 executor, _, _, _ := createExecutorEnv() 337 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) 338 339 set := "set @@vitess_metadata.app_v1= '1'" 340 _, err := executor.Execute(ctx, "TestExecute", session, set, nil) 341 assert.NoError(t, err, "%s error: %v", set, err) 342 343 show := `show vitess_metadata variables like 'app\\_%'` 344 result, _ := executor.Execute(ctx, "TestExecute", session, show, nil) 345 assert.Len(t, result.Rows, 1) 346 347 // Fails if deleting key that doesn't exist 348 delQuery := "set @@vitess_metadata.doesn't_exist=''" 349 _, err = executor.Execute(ctx, "TestExecute", session, delQuery, nil) 350 assert.True(t, topo.IsErrType(err, topo.NoNode)) 351 352 // Delete existing key, show should fail given the node doesn't exist 353 delQuery = "set @@vitess_metadata.app_v1=''" 354 _, err = executor.Execute(ctx, "TestExecute", session, delQuery, nil) 355 assert.NoError(t, err) 356 357 show = `show vitess_metadata variables like 'app\\_%'` 358 _, err = executor.Execute(ctx, "TestExecute", session, show, nil) 359 assert.True(t, topo.IsErrType(err, topo.NoNode)) 360 } 361 362 func TestExecutorAutocommit(t *testing.T) { 363 executor, _, _, sbclookup := createExecutorEnv() 364 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) 365 366 logChan := QueryLogger.Subscribe("Test") 367 defer QueryLogger.Unsubscribe(logChan) 368 369 // autocommit = 0 370 startCount := sbclookup.CommitCount.Get() 371 _, err := executor.Execute(ctx, "TestExecute", session, "select id from main1", nil) 372 require.NoError(t, err) 373 wantSession := &vtgatepb.Session{TargetString: "@primary", InTransaction: true, FoundRows: 1, RowCount: -1} 374 testSession := proto.Clone(session.Session).(*vtgatepb.Session) 375 testSession.ShardSessions = nil 376 utils.MustMatch(t, wantSession, testSession, "session does not match for autocommit=0") 377 378 logStats := testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1) 379 if logStats.CommitTime != 0 { 380 t.Errorf("logstats: expected zero CommitTime") 381 } 382 if logStats.RowsReturned == 0 { 383 t.Errorf("logstats: expected non-zero RowsReturned") 384 } 385 386 // autocommit = 1 387 _, err = executor.Execute(ctx, "TestExecute", session, "set autocommit=1", nil) 388 require.NoError(t, err) 389 _ = testQueryLog(t, logChan, "TestExecute", "SET", "set @@autocommit = 1", 0) 390 391 // Setting autocommit=1 commits existing transaction. 392 if got, want := sbclookup.CommitCount.Get(), startCount+1; got != want { 393 t.Errorf("Commit count: %d, want %d", got, want) 394 } 395 396 _, err = executor.Execute(ctx, "TestExecute", session, "update main1 set id=1", nil) 397 require.NoError(t, err) 398 wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@primary", FoundRows: 0, RowCount: 1} 399 utils.MustMatch(t, wantSession, session.Session, "session does not match for autocommit=1") 400 401 logStats = testQueryLog(t, logChan, "TestExecute", "UPDATE", "update main1 set id=1", 1) 402 assert.NotZero(t, logStats.CommitTime, "logstats: expected non-zero CommitTime") 403 assert.NotEqual(t, uint64(0), logStats.RowsAffected, "logstats: expected non-zero RowsAffected") 404 405 // autocommit = 1, "begin" 406 session.ResetTx() 407 startCount = sbclookup.CommitCount.Get() 408 _, err = executor.Execute(ctx, "TestExecute", session, "begin", nil) 409 require.NoError(t, err) 410 _ = testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0) 411 412 _, err = executor.Execute(ctx, "TestExecute", session, "update main1 set id=1", nil) 413 require.NoError(t, err) 414 wantSession = &vtgatepb.Session{InTransaction: true, Autocommit: true, TargetString: "@primary", FoundRows: 0, RowCount: 1} 415 testSession = proto.Clone(session.Session).(*vtgatepb.Session) 416 testSession.ShardSessions = nil 417 utils.MustMatch(t, wantSession, testSession, "session does not match for autocommit=1") 418 if got, want := sbclookup.CommitCount.Get(), startCount; got != want { 419 t.Errorf("Commit count: %d, want %d", got, want) 420 } 421 422 logStats = testQueryLog(t, logChan, "TestExecute", "UPDATE", "update main1 set id=1", 1) 423 if logStats.CommitTime != 0 { 424 t.Errorf("logstats: expected zero CommitTime") 425 } 426 if logStats.RowsAffected == 0 { 427 t.Errorf("logstats: expected non-zero RowsAffected") 428 } 429 430 _, err = executor.Execute(ctx, "TestExecute", session, "commit", nil) 431 require.NoError(t, err) 432 wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@primary"} 433 if !proto.Equal(session.Session, wantSession) { 434 t.Errorf("autocommit=1: %v, want %v", session.Session, wantSession) 435 } 436 if got, want := sbclookup.CommitCount.Get(), startCount+1; got != want { 437 t.Errorf("Commit count: %d, want %d", got, want) 438 } 439 _ = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1) 440 441 // transition autocommit from 0 to 1 in the middle of a transaction. 442 startCount = sbclookup.CommitCount.Get() 443 session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) 444 _, err = executor.Execute(ctx, "TestExecute", session, "begin", nil) 445 require.NoError(t, err) 446 _, err = executor.Execute(ctx, "TestExecute", session, "update main1 set id=1", nil) 447 require.NoError(t, err) 448 if got, want := sbclookup.CommitCount.Get(), startCount; got != want { 449 t.Errorf("Commit count: %d, want %d", got, want) 450 } 451 _, err = executor.Execute(ctx, "TestExecute", session, "set autocommit=1", nil) 452 require.NoError(t, err) 453 wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@primary"} 454 if !proto.Equal(session.Session, wantSession) { 455 t.Errorf("autocommit=1: %v, want %v", session.Session, wantSession) 456 } 457 if got, want := sbclookup.CommitCount.Get(), startCount+1; got != want { 458 t.Errorf("Commit count: %d, want %d", got, want) 459 } 460 } 461 462 func TestExecutorShowColumns(t *testing.T) { 463 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 464 session := NewSafeSession(&vtgatepb.Session{TargetString: ""}) 465 466 queries := []string{ 467 "SHOW COLUMNS FROM `user` in `TestExecutor`", 468 "show columns from `user` in `TestExecutor`", 469 "ShOw CoLuMnS fRoM `user` iN `TestExecutor`", 470 "SHOW columns FROM `user` in `TestExecutor`", 471 } 472 for _, query := range queries { 473 t.Run(query, func(t *testing.T) { 474 _, err := executor.Execute(ctx, "TestExecute", session, query, nil) 475 require.NoError(t, err) 476 477 wantQueries := []*querypb.BoundQuery{{ 478 Sql: "show columns from `user`", 479 BindVariables: map[string]*querypb.BindVariable{}, 480 }} 481 482 assert.Equal(t, wantQueries, sbc1.Queries, "sbc1.Queries") 483 assert.Empty(t, sbc1.BatchQueries, "sbc1.BatchQueries") 484 assert.Empty(t, sbc2.Queries, "sbc2.Queries") 485 assert.Empty(t, sbc2.BatchQueries, "sbc2.BatchQueries") 486 assert.Empty(t, sbclookup.Queries, "sbclookup.Queries") 487 assert.Empty(t, sbclookup.BatchQueries, "sbclookup.BatchQueries") 488 489 sbc1.Queries = nil 490 sbc2.Queries = nil 491 sbclookup.Queries = nil 492 sbc1.BatchQueries = nil 493 sbc2.BatchQueries = nil 494 sbclookup.BatchQueries = nil 495 }) 496 } 497 498 } 499 500 func sortString(w string) string { 501 s := strings.Split(w, "") 502 sort.Strings(s) 503 return strings.Join(s, "") 504 } 505 506 func assertMatchesNoOrder(t *testing.T, expected, got string) { 507 t.Helper() 508 if sortString(expected) != sortString(got) { 509 t.Errorf("for query: expected \n%s \nbut actual \n%s", expected, got) 510 } 511 } 512 513 func TestExecutorShow(t *testing.T) { 514 executor, _, _, sbclookup := createExecutorEnv() 515 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 516 517 for _, query := range []string{"show vitess_keyspaces", "show keyspaces"} { 518 qr, err := executor.Execute(ctx, "TestExecute", session, query, nil) 519 require.NoError(t, err) 520 assertMatchesNoOrder(t, `[[VARCHAR("TestUnsharded")] [VARCHAR("TestMultiCol")] [VARCHAR("TestXBadVSchema")] [VARCHAR("TestXBadSharding")] [VARCHAR("TestExecutor")]]`, fmt.Sprintf("%v", qr.Rows)) 521 } 522 523 for _, query := range []string{"show databases", "show DATABASES", "show schemas", "show SCHEMAS"} { 524 qr, err := executor.Execute(ctx, "TestExecute", session, query, nil) 525 require.NoError(t, err) 526 // Showing default tables (5+4[default]) 527 assertMatchesNoOrder(t, `[[VARCHAR("TestUnsharded")] [VARCHAR("TestMultiCol")] [VARCHAR("TestXBadVSchema")] [VARCHAR("TestXBadSharding")] [VARCHAR("TestExecutor")]] [VARCHAR("information_schema")] [VARCHAR("mysql")] [VARCHAR("sys")] [VARCHAR("performance_schema")]`, fmt.Sprintf("%v", qr.Rows)) 528 } 529 530 _, err := executor.Execute(ctx, "TestExecute", session, "show variables", nil) 531 require.NoError(t, err) 532 _, err = executor.Execute(ctx, "TestExecute", session, "show collation", nil) 533 require.NoError(t, err) 534 _, err = executor.Execute(ctx, "TestExecute", session, "show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'", nil) 535 require.NoError(t, err) 536 537 _, err = executor.Execute(ctx, "TestExecute", session, "use @primary", nil) 538 require.NoError(t, err) 539 _, err = executor.Execute(ctx, "TestExecute", session, "show tables", nil) 540 assert.EqualError(t, err, errNoKeyspace.Error(), "'show tables' should fail without a keyspace") 541 assert.Empty(t, sbclookup.Queries, "sbclookup unexpectedly has queries already") 542 543 showResults := &sqltypes.Result{ 544 Fields: []*querypb.Field{ 545 {Name: "Tables_in_keyspace", Type: sqltypes.VarChar}, 546 }, 547 RowsAffected: 1, 548 InsertID: 0, 549 Rows: [][]sqltypes.Value{{ 550 sqltypes.NewVarChar("some_table"), 551 }}, 552 } 553 sbclookup.SetResults([]*sqltypes.Result{showResults}) 554 555 query := fmt.Sprintf("show tables from %v", KsTestUnsharded) 556 qr, err := executor.Execute(ctx, "TestExecute", session, query, nil) 557 require.NoError(t, err) 558 559 assert.Equal(t, 1, len(sbclookup.Queries), "Tablet should have received one 'show' query. Instead received: %v", sbclookup.Queries) 560 lastQuery := sbclookup.Queries[len(sbclookup.Queries)-1].Sql 561 want := "show tables" 562 assert.Equal(t, want, lastQuery, "Got: %v, want %v", lastQuery, want) 563 564 wantqr := showResults 565 utils.MustMatch(t, wantqr, qr, fmt.Sprintf("unexpected results running query: %s", query)) 566 567 wantErrNoTable := "table unknown_table not found" 568 _, err = executor.Execute(ctx, "TestExecute", session, "show create table unknown_table", nil) 569 assert.EqualErrorf(t, err, wantErrNoTable, "Got: %v. Want: %v", wantErrNoTable) 570 571 // SHOW CREATE table using vschema to find keyspace. 572 _, err = executor.Execute(ctx, "TestExecute", session, "show create table user_seq", nil) 573 require.NoError(t, err) 574 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 575 wantQuery := "show create table user_seq" 576 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 577 578 // SHOW CREATE table with query-provided keyspace 579 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show create table %v.unknown", KsTestUnsharded), nil) 580 require.NoError(t, err) 581 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 582 wantQuery = "show create table unknown" 583 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 584 585 // SHOW KEYS with two different syntax 586 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show keys from %v.unknown", KsTestUnsharded), nil) 587 require.NoError(t, err) 588 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 589 wantQuery = "show indexes from unknown" 590 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 591 592 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show keys from unknown from %v", KsTestUnsharded), nil) 593 require.NoError(t, err) 594 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 595 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 596 597 // SHOW INDEX with two different syntax 598 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show index from %v.unknown", KsTestUnsharded), nil) 599 require.NoError(t, err) 600 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 601 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 602 603 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show index from unknown from %v", KsTestUnsharded), nil) 604 require.NoError(t, err) 605 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 606 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 607 608 // SHOW INDEXES with two different syntax 609 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show indexes from %v.unknown", KsTestUnsharded), nil) 610 require.NoError(t, err) 611 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 612 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 613 614 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show indexes from unknown from %v", KsTestUnsharded), nil) 615 require.NoError(t, err) 616 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 617 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 618 619 // SHOW EXTENDED {INDEX | INDEXES | KEYS} 620 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show extended index from unknown from %v", KsTestUnsharded), nil) 621 require.NoError(t, err) 622 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 623 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 624 625 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show extended indexes from unknown from %v", KsTestUnsharded), nil) 626 require.NoError(t, err) 627 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 628 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 629 630 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show extended keys from unknown from %v", KsTestUnsharded), nil) 631 require.NoError(t, err) 632 lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql 633 assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) 634 635 // Set desitation keyspace in session 636 session.TargetString = KsTestUnsharded 637 _, err = executor.Execute(ctx, "TestExecute", session, "show create table unknown", nil) 638 require.NoError(t, err) 639 640 _, err = executor.Execute(ctx, "TestExecute", session, "show full columns from table1", nil) 641 require.NoError(t, err) 642 643 // Reset target string so other tests dont fail. 644 session.TargetString = "@primary" 645 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show full columns from unknown from %v", KsTestUnsharded), nil) 646 require.NoError(t, err) 647 648 for _, query := range []string{"show charset", "show character set"} { 649 qr, err := executor.Execute(ctx, "TestExecute", session, query, nil) 650 require.NoError(t, err) 651 wantqr := &sqltypes.Result{ 652 Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), 653 Rows: [][]sqltypes.Value{ 654 append(buildVarCharRow( 655 "utf8", 656 "UTF-8 Unicode", 657 "utf8_general_ci"), sqltypes.NewInt32(3)), 658 append(buildVarCharRow( 659 "utf8mb4", 660 "UTF-8 Unicode", 661 "utf8mb4_general_ci"), 662 sqltypes.NewInt32(4)), 663 }, 664 } 665 666 utils.MustMatch(t, wantqr, qr, query) 667 } 668 669 for _, query := range []string{"show charset like '%foo'", "show character set like 'foo%'", "show charset like 'foo%'", "show character set where foo like 'utf8'", "show character set where charset like '%foo'", "show charset where charset = '%foo'"} { 670 qr, err := executor.Execute(ctx, "TestExecute", session, query, nil) 671 require.NoError(t, err) 672 wantqr := &sqltypes.Result{ 673 Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), 674 Rows: [][]sqltypes.Value{}, 675 RowsAffected: 0, 676 } 677 678 utils.MustMatch(t, wantqr, qr, query) 679 } 680 681 for _, query := range []string{"show charset like 'utf8'", "show character set like 'utf8'", "show charset where charset = 'utf8'", "show character set where charset = 'utf8'"} { 682 qr, err := executor.Execute(ctx, "TestExecute", session, query, nil) 683 require.NoError(t, err) 684 wantqr := &sqltypes.Result{ 685 Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), 686 Rows: [][]sqltypes.Value{ 687 append(buildVarCharRow( 688 "utf8", 689 "UTF-8 Unicode", 690 "utf8_general_ci"), sqltypes.NewInt32(3)), 691 }, 692 } 693 694 utils.MustMatch(t, wantqr, qr, query) 695 } 696 697 for _, query := range []string{"show charset like 'utf8mb4'", "show character set like 'utf8mb4'", "show charset where charset = 'utf8mb4'", "show character set where charset = 'utf8mb4'"} { 698 qr, err := executor.Execute(ctx, "TestExecute", session, query, nil) 699 require.NoError(t, err) 700 wantqr := &sqltypes.Result{ 701 Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), 702 Rows: [][]sqltypes.Value{ 703 append(buildVarCharRow( 704 "utf8mb4", 705 "UTF-8 Unicode", 706 "utf8mb4_general_ci"), 707 sqltypes.NewInt32(4)), 708 }, 709 } 710 utils.MustMatch(t, wantqr, qr, query) 711 } 712 713 query = "show engines" 714 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 715 require.NoError(t, err) 716 wantqr = &sqltypes.Result{ 717 Fields: buildVarCharFields("Engine", "Support", "Comment", "Transactions", "XA", "Savepoints"), 718 Rows: [][]sqltypes.Value{ 719 buildVarCharRow( 720 "InnoDB", 721 "DEFAULT", 722 "Supports transactions, row-level locking, and foreign keys", 723 "YES", 724 "YES", 725 "YES"), 726 }, 727 } 728 utils.MustMatch(t, wantqr, qr, query) 729 730 query = "show plugins" 731 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 732 require.NoError(t, err) 733 wantqr = &sqltypes.Result{ 734 Fields: buildVarCharFields("Name", "Status", "Type", "Library", "License"), 735 Rows: [][]sqltypes.Value{ 736 buildVarCharRow( 737 "InnoDB", 738 "ACTIVE", 739 "STORAGE ENGINE", 740 "NULL", 741 "GPL"), 742 }, 743 } 744 utils.MustMatch(t, wantqr, qr, query) 745 746 for _, sql := range []string{"show session status", "show session status like 'Ssl_cipher'"} { 747 qr, err = executor.Execute(ctx, "TestExecute", session, sql, nil) 748 require.NoError(t, err) 749 wantqr = &sqltypes.Result{ 750 Fields: []*querypb.Field{ 751 {Name: "id", Type: sqltypes.Int32}, 752 {Name: "value", Type: sqltypes.VarChar}, 753 }, 754 Rows: [][]sqltypes.Value{ 755 {sqltypes.NewInt32(1), sqltypes.NewVarChar("foo")}, 756 }, 757 } 758 759 utils.MustMatch(t, wantqr, qr, sql) 760 } 761 762 // Test SHOW FULL COLUMNS FROM where query has a qualifier 763 _, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show full columns from %v.table1", KsTestUnsharded), nil) 764 require.NoError(t, err) 765 766 query = "show vitess_shards" 767 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 768 require.NoError(t, err) 769 770 // Just test for first & last. 771 qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]} 772 wantqr = &sqltypes.Result{ 773 Fields: buildVarCharFields("Shards"), 774 Rows: [][]sqltypes.Value{ 775 buildVarCharRow("TestExecutor/-20"), 776 buildVarCharRow("TestXBadVSchema/e0-"), 777 }, 778 } 779 utils.MustMatch(t, wantqr, qr, query) 780 781 query = "show vitess_shards like 'TestExecutor/%'" 782 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 783 require.NoError(t, err) 784 785 // Just test for first & last. 786 qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]} 787 wantqr = &sqltypes.Result{ 788 Fields: buildVarCharFields("Shards"), 789 Rows: [][]sqltypes.Value{ 790 buildVarCharRow("TestExecutor/-20"), 791 buildVarCharRow("TestExecutor/e0-"), 792 }, 793 } 794 utils.MustMatch(t, wantqr, qr, query) 795 796 query = "show vitess_shards like 'TestExec%/%'" 797 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 798 require.NoError(t, err) 799 800 // Just test for first & last. 801 qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]} 802 wantqr = &sqltypes.Result{ 803 Fields: buildVarCharFields("Shards"), 804 Rows: [][]sqltypes.Value{ 805 buildVarCharRow("TestExecutor/-20"), 806 buildVarCharRow("TestExecutor/e0-"), 807 }, 808 } 809 utils.MustMatch(t, wantqr, qr, query) 810 811 query = "show vitess_replication_status" 812 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 813 require.NoError(t, err) 814 qr.Rows = [][]sqltypes.Value{} 815 wantqr = &sqltypes.Result{ 816 Fields: buildVarCharFields("Keyspace", "Shard", "TabletType", "Alias", "Hostname", "ReplicationSource", "ReplicationHealth", "ReplicationLag", "ThrottlerStatus"), 817 Rows: [][]sqltypes.Value{}, 818 } 819 utils.MustMatch(t, wantqr, qr, query) 820 query = "show vitess_replication_status like 'x'" 821 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 822 require.NoError(t, err) 823 qr.Rows = [][]sqltypes.Value{} 824 wantqr = &sqltypes.Result{ 825 Fields: buildVarCharFields("Keyspace", "Shard", "TabletType", "Alias", "Hostname", "ReplicationSource", "ReplicationHealth", "ReplicationLag", "ThrottlerStatus"), 826 Rows: [][]sqltypes.Value{}, 827 } 828 utils.MustMatch(t, wantqr, qr, query) 829 830 query = "show vitess_tablets" 831 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 832 require.NoError(t, err) 833 // Just test for first & last. 834 qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]} 835 wantqr = &sqltypes.Result{ 836 Fields: buildVarCharFields("Cell", "Keyspace", "Shard", "TabletType", "State", "Alias", "Hostname", "PrimaryTermStartTime"), 837 Rows: [][]sqltypes.Value{ 838 buildVarCharRow("aa", "TestExecutor", "-20", "PRIMARY", "SERVING", "aa-0000000001", "-20", "1970-01-01T00:00:01Z"), 839 buildVarCharRow("aa", "TestXBadVSchema", "-20", "PRIMARY", "SERVING", "aa-0000000009", "random", "1970-01-01T00:00:01Z"), 840 }, 841 } 842 utils.MustMatch(t, wantqr, qr, query) 843 844 query = "show vitess_tablets like 'x'" 845 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 846 require.NoError(t, err) 847 wantqr = &sqltypes.Result{ 848 Fields: buildVarCharFields("Cell", "Keyspace", "Shard", "TabletType", "State", "Alias", "Hostname", "PrimaryTermStartTime"), 849 Rows: [][]sqltypes.Value{}, 850 } 851 utils.MustMatch(t, wantqr, qr, fmt.Sprintf("%q should be empty", query)) 852 853 query = "show vitess_tablets like '-20%'" 854 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 855 require.NoError(t, err) 856 wantqr = &sqltypes.Result{ 857 Fields: buildVarCharFields("Cell", "Keyspace", "Shard", "TabletType", "State", "Alias", "Hostname", "PrimaryTermStartTime"), 858 Rows: [][]sqltypes.Value{ 859 buildVarCharRow("aa", "TestExecutor", "-20", "PRIMARY", "SERVING", "aa-0000000001", "-20", "1970-01-01T00:00:01Z"), 860 }, 861 } 862 utils.MustMatch(t, wantqr, qr, query) 863 864 query = "show vschema vindexes" 865 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 866 require.NoError(t, err) 867 wantqr = &sqltypes.Result{ 868 Fields: buildVarCharFields("Keyspace", "Name", "Type", "Params", "Owner"), 869 Rows: [][]sqltypes.Value{ 870 buildVarCharRow("TestExecutor", "cfc", "cfc", "", ""), 871 buildVarCharRow("TestExecutor", "hash_index", "hash", "", ""), 872 buildVarCharRow("TestExecutor", "idx1", "hash", "", ""), 873 buildVarCharRow("TestExecutor", "idx_noauto", "hash", "", "noauto_table"), 874 buildVarCharRow("TestExecutor", "insert_ignore_idx", "lookup_hash", "from=fromcol; table=ins_lookup; to=tocol", "insert_ignore_test"), 875 buildVarCharRow("TestExecutor", "keyspace_id", "numeric", "", ""), 876 buildVarCharRow("TestExecutor", "krcol_unique_vdx", "keyrange_lookuper_unique", "", ""), 877 buildVarCharRow("TestExecutor", "krcol_vdx", "keyrange_lookuper", "", ""), 878 buildVarCharRow("TestExecutor", "music_user_map", "lookup_hash_unique", "from=music_id; table=music_user_map; to=user_id", "music"), 879 buildVarCharRow("TestExecutor", "name_lastname_keyspace_id_map", "lookup", "from=name,lastname; table=name_lastname_keyspace_id_map; to=keyspace_id", "user2"), 880 buildVarCharRow("TestExecutor", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"), 881 buildVarCharRow("TestExecutor", "regional_vdx", "region_experimental", "region_bytes=1", ""), 882 buildVarCharRow("TestExecutor", "t1_lkp_vdx", "consistent_lookup_unique", "from=unq_col; table=t1_lkp_idx; to=keyspace_id", "t1"), 883 buildVarCharRow("TestExecutor", "t2_erl_lu_vdx", "lookup_unique", "from=erl_lu_col; read_lock=exclusive; table=TestUnsharded.erl_lu_idx; to=keyspace_id", "t2_lookup"), 884 buildVarCharRow("TestExecutor", "t2_lu_vdx", "lookup_hash_unique", "from=lu_col; table=TestUnsharded.lu_idx; to=keyspace_id", "t2_lookup"), 885 buildVarCharRow("TestExecutor", "t2_nrl_lu_vdx", "lookup_unique", "from=nrl_lu_col; read_lock=none; table=TestUnsharded.nrl_lu_idx; to=keyspace_id", "t2_lookup"), 886 buildVarCharRow("TestExecutor", "t2_nv_lu_vdx", "lookup_unique", "from=nv_lu_col; no_verify=true; table=TestUnsharded.nv_lu_idx; to=keyspace_id", "t2_lookup"), 887 buildVarCharRow("TestExecutor", "t2_srl_lu_vdx", "lookup_unique", "from=srl_lu_col; read_lock=shared; table=TestUnsharded.srl_lu_idx; to=keyspace_id", "t2_lookup"), 888 buildVarCharRow("TestExecutor", "t2_wo_lu_vdx", "lookup_unique", "from=wo_lu_col; table=TestUnsharded.wo_lu_idx; to=keyspace_id; write_only=true", "t2_lookup"), 889 buildVarCharRow("TestMultiCol", "multicol_vdx", "multicol", "column_bytes=1,3,4; column_count=3; column_vindex=hash,binary,unicode_loose_xxhash", ""), 890 }, 891 } 892 utils.MustMatch(t, wantqr, qr, query) 893 894 query = "show vschema vindexes on TestExecutor.user" 895 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 896 require.NoError(t, err) 897 wantqr = &sqltypes.Result{ 898 Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"), 899 Rows: [][]sqltypes.Value{ 900 buildVarCharRow("Id", "hash_index", "hash", "", ""), 901 buildVarCharRow("name", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"), 902 }, 903 } 904 utils.MustMatch(t, wantqr, qr, query) 905 906 query = "show vschema vindexes on user" 907 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 908 wantErr := errNoKeyspace.Error() 909 assert.EqualError(t, err, wantErr, query) 910 911 query = "show vschema vindexes on TestExecutor.garbage" 912 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 913 wantErr = "VT05005: table 'garbage' does not exist in keyspace 'TestExecutor'" 914 assert.EqualError(t, err, wantErr, query) 915 916 query = "show vschema vindexes on user" 917 session.TargetString = "TestExecutor" 918 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 919 require.NoError(t, err) 920 wantqr = &sqltypes.Result{ 921 Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"), 922 Rows: [][]sqltypes.Value{ 923 buildVarCharRow("Id", "hash_index", "hash", "", ""), 924 buildVarCharRow("name", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"), 925 }, 926 } 927 utils.MustMatch(t, wantqr, qr, query) 928 929 query = "show vschema vindexes on user2" 930 session.TargetString = "TestExecutor" 931 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 932 require.NoError(t, err) 933 wantqr = &sqltypes.Result{ 934 Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"), 935 Rows: [][]sqltypes.Value{ 936 buildVarCharRow("id", "hash_index", "hash", "", ""), 937 buildVarCharRow("name, lastname", "name_lastname_keyspace_id_map", "lookup", "from=name,lastname; table=name_lastname_keyspace_id_map; to=keyspace_id", "user2"), 938 }, 939 } 940 utils.MustMatch(t, wantqr, qr, query) 941 942 query = "show vschema vindexes on garbage" 943 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 944 wantErr = "VT05005: table 'garbage' does not exist in keyspace 'TestExecutor'" 945 assert.EqualError(t, err, wantErr, query) 946 947 query = "show warnings" 948 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 949 require.NoError(t, err) 950 wantqr = &sqltypes.Result{ 951 Fields: []*querypb.Field{ 952 {Name: "Level", Type: sqltypes.VarChar}, 953 {Name: "Code", Type: sqltypes.Uint16}, 954 {Name: "Message", Type: sqltypes.VarChar}, 955 }, 956 Rows: [][]sqltypes.Value{}, 957 } 958 utils.MustMatch(t, wantqr, qr, query) 959 960 query = "show warnings" 961 session.Warnings = []*querypb.QueryWarning{} 962 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 963 require.NoError(t, err) 964 wantqr = &sqltypes.Result{ 965 Fields: []*querypb.Field{ 966 {Name: "Level", Type: sqltypes.VarChar}, 967 {Name: "Code", Type: sqltypes.Uint16}, 968 {Name: "Message", Type: sqltypes.VarChar}, 969 }, 970 Rows: [][]sqltypes.Value{}, 971 } 972 utils.MustMatch(t, wantqr, qr, query) 973 974 query = "show warnings" 975 session.Warnings = []*querypb.QueryWarning{ 976 {Code: mysql.ERBadTable, Message: "bad table"}, 977 {Code: mysql.EROutOfResources, Message: "ks/-40: query timed out"}, 978 } 979 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 980 require.NoError(t, err) 981 wantqr = &sqltypes.Result{ 982 Fields: []*querypb.Field{ 983 {Name: "Level", Type: sqltypes.VarChar}, 984 {Name: "Code", Type: sqltypes.Uint16}, 985 {Name: "Message", Type: sqltypes.VarChar}, 986 }, 987 988 Rows: [][]sqltypes.Value{ 989 {sqltypes.NewVarChar("Warning"), sqltypes.NewUint32(mysql.ERBadTable), sqltypes.NewVarChar("bad table")}, 990 {sqltypes.NewVarChar("Warning"), sqltypes.NewUint32(mysql.EROutOfResources), sqltypes.NewVarChar("ks/-40: query timed out")}, 991 }, 992 } 993 utils.MustMatch(t, wantqr, qr, query) 994 995 // Make sure it still works when one of the keyspaces is in a bad state 996 getSandbox("TestExecutor").SrvKeyspaceMustFail++ 997 query = "show vitess_shards" 998 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 999 require.NoError(t, err) 1000 // Just test for first & last. 1001 qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]} 1002 wantqr = &sqltypes.Result{ 1003 Fields: buildVarCharFields("Shards"), 1004 Rows: [][]sqltypes.Value{ 1005 buildVarCharRow("TestMultiCol/-20"), 1006 buildVarCharRow("TestXBadVSchema/e0-"), 1007 }, 1008 } 1009 utils.MustMatch(t, wantqr, qr, fmt.Sprintf("%s, with a bad keyspace", query)) 1010 1011 query = "show vschema tables" 1012 session = NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) 1013 qr, err = executor.Execute(ctx, "TestExecute", session, query, nil) 1014 require.NoError(t, err) 1015 wantqr = &sqltypes.Result{ 1016 Fields: buildVarCharFields("Tables"), 1017 Rows: [][]sqltypes.Value{ 1018 buildVarCharRow("dual"), 1019 buildVarCharRow("erl_lu_idx"), 1020 buildVarCharRow("ins_lookup"), 1021 buildVarCharRow("lu_idx"), 1022 buildVarCharRow("main1"), 1023 buildVarCharRow("music_user_map"), 1024 buildVarCharRow("name_lastname_keyspace_id_map"), 1025 buildVarCharRow("name_user_map"), 1026 buildVarCharRow("nrl_lu_idx"), 1027 buildVarCharRow("nv_lu_idx"), 1028 buildVarCharRow("simple"), 1029 buildVarCharRow("srl_lu_idx"), 1030 buildVarCharRow("user_msgs"), 1031 buildVarCharRow("user_seq"), 1032 buildVarCharRow("wo_lu_idx"), 1033 buildVarCharRow("zip_detail"), 1034 }, 1035 } 1036 utils.MustMatch(t, wantqr, qr, query) 1037 1038 query = "show vschema tables" 1039 session = NewSafeSession(&vtgatepb.Session{}) 1040 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 1041 want = errNoKeyspace.Error() 1042 assert.EqualError(t, err, want, query) 1043 1044 query = "show 10" 1045 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 1046 want = "syntax error at position 8 near '10'" 1047 assert.EqualError(t, err, want, query) 1048 1049 query = "show vschema tables" 1050 session = NewSafeSession(&vtgatepb.Session{TargetString: "no_such_keyspace"}) 1051 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 1052 want = "VT05003: unknown database 'no_such_keyspace' in vschema" 1053 assert.EqualError(t, err, want, query) 1054 1055 query = "show vitess_migrations" 1056 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 1057 want = "VT05003: unknown database 'no_such_keyspace' in vschema" 1058 assert.EqualError(t, err, want, query) 1059 1060 query = "show vitess_migrations from ks like '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90'" 1061 _, err = executor.Execute(ctx, "TestExecute", session, query, nil) 1062 want = "VT05003: unknown database 'ks' in vschema" 1063 assert.EqualError(t, err, want, query) 1064 } 1065 1066 func TestExecutorShowTargeted(t *testing.T) { 1067 executor, _, sbc2, _ := createExecutorEnv() 1068 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/40-60"}) 1069 1070 queries := []string{ 1071 "show databases", 1072 "show variables like 'read_only'", 1073 "show collation", 1074 "show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'", 1075 "show tables", 1076 fmt.Sprintf("show tables from %v", KsTestUnsharded), 1077 "show create table user_seq", 1078 "show full columns from table1", 1079 "show plugins", 1080 "show warnings", 1081 } 1082 1083 for _, sql := range queries { 1084 _, err := executor.Execute(ctx, "TestExecutorShowTargeted", session, sql, nil) 1085 require.NoError(t, err) 1086 assert.NotZero(t, len(sbc2.Queries), "Tablet should have received 'show' query") 1087 lastQuery := sbc2.Queries[len(sbc2.Queries)-1].Sql 1088 assert.Equal(t, sql, lastQuery, "Got: %v, want %v", lastQuery, sql) 1089 } 1090 } 1091 1092 func TestExecutorUse(t *testing.T) { 1093 executor, _, _, _ := createExecutorEnv() 1094 session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary"}) 1095 1096 stmts := []string{ 1097 "use TestExecutor", 1098 "use `TestExecutor:-80@primary`", 1099 } 1100 want := []string{ 1101 "TestExecutor", 1102 "TestExecutor:-80@primary", 1103 } 1104 for i, stmt := range stmts { 1105 _, err := executor.Execute(ctx, "TestExecute", session, stmt, nil) 1106 if err != nil { 1107 t.Error(err) 1108 } 1109 wantSession := &vtgatepb.Session{Autocommit: true, TargetString: want[i], RowCount: -1} 1110 utils.MustMatch(t, wantSession, session.Session, "session does not match") 1111 } 1112 1113 _, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use 1", nil) 1114 wantErr := "syntax error at position 6 near '1'" 1115 if err == nil || err.Error() != wantErr { 1116 t.Errorf("got: %v, want %v", err, wantErr) 1117 } 1118 1119 _, err = executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use UnexistentKeyspace", nil) 1120 require.EqualError(t, err, "VT05003: unknown database 'UnexistentKeyspace' in vschema") 1121 } 1122 1123 func TestExecutorComment(t *testing.T) { 1124 executor, _, _, _ := createExecutorEnv() 1125 1126 stmts := []string{ 1127 "/*! SET autocommit=1*/", 1128 "/*!50708 SET @x=5000*/", 1129 } 1130 wantResult := &sqltypes.Result{} 1131 1132 for _, stmt := range stmts { 1133 gotResult, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) 1134 if err != nil { 1135 t.Error(err) 1136 } 1137 if !gotResult.Equal(wantResult) { 1138 t.Errorf("Exec %s: %v, want %v", stmt, gotResult, wantResult) 1139 } 1140 } 1141 } 1142 1143 func TestExecutorOther(t *testing.T) { 1144 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 1145 1146 type cnts struct { 1147 Sbc1Cnt int64 1148 Sbc2Cnt int64 1149 SbcLookupCnt int64 1150 } 1151 1152 tcs := []struct { 1153 targetStr string 1154 1155 hasNoKeyspaceErr bool 1156 hasDestinationShardErr bool 1157 wantCnts cnts 1158 }{ 1159 { 1160 targetStr: "", 1161 hasNoKeyspaceErr: true, 1162 }, 1163 { 1164 targetStr: "TestExecutor[-]", 1165 hasDestinationShardErr: true, 1166 }, 1167 { 1168 targetStr: KsTestUnsharded, 1169 wantCnts: cnts{ 1170 Sbc1Cnt: 0, 1171 Sbc2Cnt: 0, 1172 SbcLookupCnt: 1, 1173 }, 1174 }, 1175 { 1176 targetStr: "TestExecutor", 1177 wantCnts: cnts{ 1178 Sbc1Cnt: 1, 1179 Sbc2Cnt: 0, 1180 SbcLookupCnt: 0, 1181 }, 1182 }, 1183 { 1184 targetStr: "TestExecutor/-20", 1185 wantCnts: cnts{ 1186 Sbc1Cnt: 1, 1187 Sbc2Cnt: 0, 1188 SbcLookupCnt: 0, 1189 }, 1190 }, 1191 { 1192 targetStr: "TestExecutor[00]", 1193 wantCnts: cnts{ 1194 Sbc1Cnt: 1, 1195 Sbc2Cnt: 0, 1196 SbcLookupCnt: 0, 1197 }, 1198 }, 1199 } 1200 1201 stmts := []string{ 1202 "analyze table t1", 1203 "describe select * from t1", 1204 "explain select * from t1", 1205 "repair table t1", 1206 "optimize table t1", 1207 } 1208 1209 for _, stmt := range stmts { 1210 for _, tc := range tcs { 1211 t.Run(fmt.Sprintf("%s-%s", stmt, tc.targetStr), func(t *testing.T) { 1212 sbc1.ExecCount.Set(0) 1213 sbc2.ExecCount.Set(0) 1214 sbclookup.ExecCount.Set(0) 1215 1216 _, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) 1217 if tc.hasNoKeyspaceErr { 1218 assert.Error(t, err, errNoKeyspace) 1219 } else if tc.hasDestinationShardErr { 1220 assert.Errorf(t, err, "Destination can only be a single shard for statement: %s", stmt) 1221 } else { 1222 assert.NoError(t, err) 1223 } 1224 1225 utils.MustMatch(t, tc.wantCnts, cnts{ 1226 Sbc1Cnt: sbc1.ExecCount.Get(), 1227 Sbc2Cnt: sbc2.ExecCount.Get(), 1228 SbcLookupCnt: sbclookup.ExecCount.Get(), 1229 }) 1230 }) 1231 } 1232 } 1233 } 1234 1235 func TestExecutorDDL(t *testing.T) { 1236 logChan := QueryLogger.Subscribe("Test") 1237 defer QueryLogger.Unsubscribe(logChan) 1238 1239 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 1240 1241 type cnts struct { 1242 Sbc1Cnt int64 1243 Sbc2Cnt int64 1244 SbcLookupCnt int64 1245 } 1246 1247 tcs := []struct { 1248 targetStr string 1249 1250 hasNoKeyspaceErr bool 1251 shardQueryCnt int 1252 wantCnts cnts 1253 }{ 1254 { 1255 targetStr: "", 1256 hasNoKeyspaceErr: true, 1257 }, 1258 { 1259 targetStr: KsTestUnsharded, 1260 shardQueryCnt: 1, 1261 wantCnts: cnts{ 1262 Sbc1Cnt: 0, 1263 Sbc2Cnt: 0, 1264 SbcLookupCnt: 1, 1265 }, 1266 }, 1267 { 1268 targetStr: "TestExecutor", 1269 shardQueryCnt: 8, 1270 wantCnts: cnts{ 1271 Sbc1Cnt: 1, 1272 Sbc2Cnt: 1, 1273 SbcLookupCnt: 0, 1274 }, 1275 }, 1276 { 1277 targetStr: "TestExecutor/-20", 1278 shardQueryCnt: 1, 1279 wantCnts: cnts{ 1280 Sbc1Cnt: 1, 1281 Sbc2Cnt: 0, 1282 SbcLookupCnt: 0, 1283 }, 1284 }, 1285 } 1286 1287 stmts := []string{ 1288 "create table t2(id bigint primary key)", 1289 "alter table t2 add primary key (id)", 1290 "rename table t2 to t3", 1291 "truncate table t2", 1292 "drop table t2", 1293 `create table test_partitioned ( 1294 id bigint, 1295 date_create int, 1296 primary key(id) 1297 ) Engine=InnoDB /*!50100 PARTITION BY RANGE (date_create) 1298 (PARTITION p2018_06_14 VALUES LESS THAN (1528959600) ENGINE = InnoDB, 1299 PARTITION p2018_06_15 VALUES LESS THAN (1529046000) ENGINE = InnoDB, 1300 PARTITION p2018_06_16 VALUES LESS THAN (1529132400) ENGINE = InnoDB, 1301 PARTITION p2018_06_17 VALUES LESS THAN (1529218800) ENGINE = InnoDB)*/`, 1302 } 1303 1304 for _, stmt := range stmts { 1305 for _, tc := range tcs { 1306 sbc1.ExecCount.Set(0) 1307 sbc2.ExecCount.Set(0) 1308 sbclookup.ExecCount.Set(0) 1309 stmtType := "DDL" 1310 _, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) 1311 if tc.hasNoKeyspaceErr { 1312 require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail: %q", stmt) 1313 stmtType = "" // For error case, plan is not generated to query log will not contain any stmtType. 1314 } else { 1315 require.NoError(t, err, "did not expect error for query: %q", stmt) 1316 } 1317 1318 diff := cmp.Diff(tc.wantCnts, cnts{ 1319 Sbc1Cnt: sbc1.ExecCount.Get(), 1320 Sbc2Cnt: sbc2.ExecCount.Get(), 1321 SbcLookupCnt: sbclookup.ExecCount.Get(), 1322 }) 1323 if diff != "" { 1324 t.Errorf("stmt: %s\ntc: %+v\n-want,+got:\n%s", stmt, tc, diff) 1325 } 1326 1327 testQueryLog(t, logChan, "TestExecute", stmtType, stmt, tc.shardQueryCnt) 1328 } 1329 } 1330 1331 stmts2 := []struct { 1332 input string 1333 hasErr bool 1334 }{ 1335 {input: "create table t1(id bigint primary key)", hasErr: false}, 1336 {input: "drop table t1", hasErr: false}, 1337 {input: "drop table t2", hasErr: true}, 1338 {input: "drop view t1", hasErr: false}, 1339 {input: "drop view t2", hasErr: true}, 1340 {input: "alter view t1 as select * from t1", hasErr: false}, 1341 {input: "alter view t2 as select * from t1", hasErr: true}, 1342 } 1343 1344 for _, stmt := range stmts2 { 1345 sbc1.ExecCount.Set(0) 1346 sbc2.ExecCount.Set(0) 1347 sbclookup.ExecCount.Set(0) 1348 _, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: ""}), stmt.input, nil) 1349 if stmt.hasErr { 1350 require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail") 1351 testQueryLog(t, logChan, "TestExecute", "", stmt.input, 0) 1352 } else { 1353 require.NoError(t, err) 1354 testQueryLog(t, logChan, "TestExecute", "DDL", stmt.input, 8) 1355 } 1356 } 1357 } 1358 1359 func TestExecutorDDLFk(t *testing.T) { 1360 executor, _, _, sbc := createExecutorEnv() 1361 1362 mName := "TestExecutorDDLFk" 1363 stmts := []string{ 1364 "create table t1(id bigint primary key, foreign key (id) references t2(id))", 1365 "alter table t2 add foreign key (id) references t1(id) on delete cascade", 1366 } 1367 1368 for _, stmt := range stmts { 1369 for _, fkMode := range []string{"allow", "disallow"} { 1370 t.Run(stmt+fkMode, func(t *testing.T) { 1371 sbc.ExecCount.Set(0) 1372 foreignKeyMode = fkMode 1373 _, err := executor.Execute(ctx, mName, NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) 1374 if fkMode == "allow" { 1375 require.NoError(t, err) 1376 require.EqualValues(t, 1, sbc.ExecCount.Get()) 1377 } else { 1378 require.Error(t, err) 1379 require.Contains(t, err.Error(), "foreign key constraints are not allowed") 1380 } 1381 }) 1382 } 1383 } 1384 } 1385 1386 func TestExecutorAlterVSchemaKeyspace(t *testing.T) { 1387 vschemaacl.AuthorizedDDLUsers = "%" 1388 defer func() { 1389 vschemaacl.AuthorizedDDLUsers = "" 1390 }() 1391 executor, _, _, _ := createExecutorEnv() 1392 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) 1393 1394 vschemaUpdates := make(chan *vschemapb.SrvVSchema, 2) 1395 executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool { 1396 vschemaUpdates <- vschema 1397 return true 1398 }) 1399 1400 vschema := <-vschemaUpdates 1401 _, ok := vschema.Keyspaces["TestExecutor"].Vindexes["test_vindex"] 1402 if ok { 1403 t.Fatalf("test_vindex should not exist in original vschema") 1404 } 1405 1406 stmt := "alter vschema create vindex TestExecutor.test_vindex using hash" 1407 _, err := executor.Execute(ctx, "TestExecute", session, stmt, nil) 1408 require.NoError(t, err) 1409 1410 _, vindex := waitForVindex(t, "TestExecutor", "test_vindex", vschemaUpdates, executor) 1411 assert.Equal(t, vindex.Type, "hash") 1412 } 1413 1414 func TestExecutorCreateVindexDDL(t *testing.T) { 1415 vschemaacl.AuthorizedDDLUsers = "%" 1416 defer func() { 1417 vschemaacl.AuthorizedDDLUsers = "" 1418 }() 1419 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 1420 ks := "TestExecutor" 1421 1422 vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4) 1423 executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool { 1424 vschemaUpdates <- vschema 1425 return true 1426 }) 1427 1428 vschema := <-vschemaUpdates 1429 _, ok := vschema.Keyspaces[ks].Vindexes["test_vindex"] 1430 if ok { 1431 t.Fatalf("test_vindex should not exist in original vschema") 1432 } 1433 1434 session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) 1435 stmt := "alter vschema create vindex test_vindex using hash" 1436 _, err := executor.Execute(ctx, "TestExecute", session, stmt, nil) 1437 require.NoError(t, err) 1438 1439 _, vindex := waitForVindex(t, ks, "test_vindex", vschemaUpdates, executor) 1440 if vindex == nil || vindex.Type != "hash" { 1441 t.Errorf("updated vschema did not contain test_vindex") 1442 } 1443 1444 _, err = executor.Execute(ctx, "TestExecute", session, stmt, nil) 1445 wantErr := "vindex test_vindex already exists in keyspace TestExecutor" 1446 if err == nil || err.Error() != wantErr { 1447 t.Errorf("create duplicate vindex: %v, want %s", err, wantErr) 1448 } 1449 select { 1450 case <-vschemaUpdates: 1451 t.Error("vschema should not be updated on error") 1452 default: 1453 } 1454 1455 // Create a new vschema keyspace implicitly by creating a vindex with a different 1456 // target in the session 1457 // ksNew := "test_new_keyspace" 1458 session = NewSafeSession(&vtgatepb.Session{TargetString: ks}) 1459 stmt = "alter vschema create vindex test_vindex2 using hash" 1460 _, err = executor.Execute(ctx, "TestExecute", session, stmt, nil) 1461 if err != nil { 1462 t.Fatalf("error in %s: %v", stmt, err) 1463 } 1464 1465 vschema, vindex = waitForVindex(t, ks, "test_vindex2", vschemaUpdates, executor) 1466 if vindex.Type != "hash" { 1467 t.Errorf("vindex type %s not hash", vindex.Type) 1468 } 1469 keyspace, ok := vschema.Keyspaces[ks] 1470 if !ok || !keyspace.Sharded { 1471 t.Errorf("keyspace should have been created with Sharded=true") 1472 } 1473 1474 // No queries should have gone to any tablets 1475 wantCount := []int64{0, 0, 0} 1476 gotCount := []int64{ 1477 sbc1.ExecCount.Get(), 1478 sbc2.ExecCount.Get(), 1479 sbclookup.ExecCount.Get(), 1480 } 1481 require.Equal(t, wantCount, gotCount) 1482 } 1483 1484 func TestExecutorAddDropVschemaTableDDL(t *testing.T) { 1485 vschemaacl.AuthorizedDDLUsers = "%" 1486 defer func() { 1487 vschemaacl.AuthorizedDDLUsers = "" 1488 }() 1489 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 1490 ks := KsTestUnsharded 1491 1492 vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4) 1493 executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool { 1494 vschemaUpdates <- vschema 1495 return true 1496 }) 1497 1498 vschema := <-vschemaUpdates 1499 _, ok := vschema.Keyspaces[ks].Tables["test_table"] 1500 if ok { 1501 t.Fatalf("test_table should not exist in original vschema") 1502 } 1503 1504 var vschemaTables []string 1505 for t := range vschema.Keyspaces[ks].Tables { 1506 vschemaTables = append(vschemaTables, t) 1507 } 1508 1509 session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) 1510 stmt := "alter vschema add table test_table" 1511 _, err := executor.Execute(ctx, "TestExecute", session, stmt, nil) 1512 require.NoError(t, err) 1513 _ = waitForVschemaTables(t, ks, append([]string{"test_table"}, vschemaTables...), executor) 1514 1515 stmt = "alter vschema add table test_table2" 1516 _, err = executor.Execute(ctx, "TestExecute", session, stmt, nil) 1517 require.NoError(t, err) 1518 _ = waitForVschemaTables(t, ks, append([]string{"test_table", "test_table2"}, vschemaTables...), executor) 1519 1520 // Should fail adding a table on a sharded keyspace 1521 session = NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 1522 stmt = "alter vschema add table test_table" 1523 _, err = executor.Execute(ctx, "TestExecute", session, stmt, nil) 1524 require.EqualError(t, err, "add vschema table: unsupported on sharded keyspace TestExecutor") 1525 1526 // No queries should have gone to any tablets 1527 wantCount := []int64{0, 0, 0} 1528 gotCount := []int64{ 1529 sbc1.ExecCount.Get(), 1530 sbc2.ExecCount.Get(), 1531 sbclookup.ExecCount.Get(), 1532 } 1533 utils.MustMatch(t, wantCount, gotCount, "") 1534 } 1535 1536 func TestExecutorVindexDDLACL(t *testing.T) { 1537 executor, _, _, _ := createExecutorEnv() 1538 ks := "TestExecutor" 1539 session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) 1540 1541 ctxRedUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "redUser"}) 1542 ctxBlueUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "blueUser"}) 1543 1544 // test that by default no users can perform the operation 1545 stmt := "alter vschema create vindex test_hash using hash" 1546 _, err := executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil) 1547 require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`) 1548 1549 _, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil) 1550 require.EqualError(t, err, `User 'blueUser' is not authorized to perform vschema operations`) 1551 1552 // test when all users are enabled 1553 vschemaacl.AuthorizedDDLUsers = "%" 1554 vschemaacl.Init() 1555 _, err = executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil) 1556 if err != nil { 1557 t.Errorf("unexpected error '%v'", err) 1558 } 1559 stmt = "alter vschema create vindex test_hash2 using hash" 1560 _, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil) 1561 if err != nil { 1562 t.Errorf("unexpected error '%v'", err) 1563 } 1564 1565 // test when only one user is enabled 1566 vschemaacl.AuthorizedDDLUsers = "orangeUser, blueUser, greenUser" 1567 vschemaacl.Init() 1568 _, err = executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil) 1569 require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`) 1570 1571 stmt = "alter vschema create vindex test_hash3 using hash" 1572 _, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil) 1573 if err != nil { 1574 t.Errorf("unexpected error '%v'", err) 1575 } 1576 1577 // restore the disallowed state 1578 vschemaacl.AuthorizedDDLUsers = "" 1579 } 1580 1581 func TestExecutorUnrecognized(t *testing.T) { 1582 executor, _, _, _ := createExecutorEnv() 1583 _, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "invalid statement", nil) 1584 require.Error(t, err, "unrecognized statement: invalid statement'") 1585 } 1586 1587 // TestVSchemaStats makes sure the building and displaying of the 1588 // VSchemaStats works. 1589 func TestVSchemaStats(t *testing.T) { 1590 r, _, _, _ := createExecutorEnv() 1591 1592 stats := r.VSchemaStats() 1593 1594 templ := template.New("") 1595 templ, err := templ.Parse(VSchemaTemplate) 1596 if err != nil { 1597 t.Fatalf("error parsing template: %v", err) 1598 } 1599 wr := &bytes.Buffer{} 1600 if err := templ.Execute(wr, stats); err != nil { 1601 t.Fatalf("error executing template: %v", err) 1602 } 1603 result := wr.String() 1604 if !strings.Contains(result, "<td>TestXBadSharding</td>") || 1605 !strings.Contains(result, "<td>TestUnsharded</td>") { 1606 t.Errorf("invalid html result: %v", result) 1607 } 1608 } 1609 1610 var pv = querypb.ExecuteOptions_Gen4 1611 1612 func TestGetPlanUnnormalized(t *testing.T) { 1613 r, _, _, _ := createExecutorEnv() 1614 emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1615 unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1616 1617 query1 := "select * from music_user_map where id = 1" 1618 plan1, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1619 wantSQL := query1 + " /* comment */" 1620 if logStats1.SQL != wantSQL { 1621 t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats1.SQL) 1622 } 1623 1624 plan2, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1625 if plan1 != plan2 { 1626 t.Errorf("getPlan(query1): plans must be equal: %p %p", plan1, plan2) 1627 } 1628 want := []string{ 1629 "@unknown:" + query1, 1630 } 1631 assertCacheContains(t, r, want) 1632 if logStats2.SQL != wantSQL { 1633 t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats2.SQL) 1634 } 1635 plan3, logStats3 := getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1636 if plan1 == plan3 { 1637 t.Errorf("getPlan(query1, ks): plans must not be equal: %p %p", plan1, plan3) 1638 } 1639 if logStats3.SQL != wantSQL { 1640 t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats3.SQL) 1641 } 1642 plan4, logStats4 := getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1643 if plan3 != plan4 { 1644 t.Errorf("getPlan(query1, ks): plans must be equal: %p %p", plan3, plan4) 1645 } 1646 want = []string{ 1647 KsTestUnsharded + "@unknown:" + query1, 1648 "@unknown:" + query1, 1649 } 1650 assertCacheContains(t, r, want) 1651 if logStats4.SQL != wantSQL { 1652 t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats4.SQL) 1653 } 1654 } 1655 1656 func assertCacheSize(t *testing.T, c cache.Cache, expected int) { 1657 t.Helper() 1658 var size int 1659 c.ForEach(func(_ any) bool { 1660 size++ 1661 return true 1662 }) 1663 if size != expected { 1664 t.Errorf("getPlan() expected cache to have size %d, but got: %d", expected, size) 1665 } 1666 } 1667 1668 func assertCacheContains(t *testing.T, e *Executor, want []string) { 1669 t.Helper() 1670 for _, wantKey := range want { 1671 if _, ok := e.debugGetPlan(wantKey); !ok { 1672 t.Errorf("missing key in plan cache: %v", wantKey) 1673 } 1674 } 1675 } 1676 1677 func getPlanCached(t *testing.T, e *Executor, vcursor *vcursorImpl, sql string, comments sqlparser.MarginComments, bindVars map[string]*querypb.BindVariable, skipQueryPlanCache bool) (*engine.Plan, *logstats.LogStats) { 1678 logStats := logstats.NewLogStats(ctx, "Test", "", "", nil) 1679 plan, _, err := e.getPlan(context.Background(), vcursor, sql, comments, bindVars, &SafeSession{ 1680 Session: &vtgatepb.Session{Options: &querypb.ExecuteOptions{SkipQueryPlanCache: skipQueryPlanCache}}, 1681 }, logStats) 1682 require.NoError(t, err) 1683 1684 // Wait for cache to settle 1685 e.plans.Wait() 1686 return plan, logStats 1687 } 1688 1689 func TestGetPlanCacheUnnormalized(t *testing.T) { 1690 r, _, _, _ := createExecutorEnv() 1691 emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1692 query1 := "select * from music_user_map where id = 1" 1693 1694 _, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true) 1695 assertCacheSize(t, r.plans, 0) 1696 1697 wantSQL := query1 + " /* comment */" 1698 if logStats1.SQL != wantSQL { 1699 t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats1.SQL) 1700 } 1701 1702 _, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment 2 */"), map[string]*querypb.BindVariable{}, false) 1703 assertCacheSize(t, r.plans, 1) 1704 1705 wantSQL = query1 + " /* comment 2 */" 1706 if logStats2.SQL != wantSQL { 1707 t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats2.SQL) 1708 } 1709 1710 // Skip cache using directive 1711 r, _, _, _ = createExecutorEnv() 1712 unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1713 1714 query1 = "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" 1715 getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1716 assertCacheSize(t, r.plans, 0) 1717 1718 query1 = "insert into user(id) values (1), (2)" 1719 getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1720 assertCacheSize(t, r.plans, 1) 1721 1722 // the target string will be resolved and become part of the plan cache key, which adds a new entry 1723 ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1724 getPlanCached(t, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1725 assertCacheSize(t, r.plans, 2) 1726 1727 // the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above 1728 ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1729 getPlanCached(t, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1730 assertCacheSize(t, r.plans, 2) 1731 } 1732 1733 func TestGetPlanCacheNormalized(t *testing.T) { 1734 r, _, _, _ := createExecutorEnv() 1735 r.normalize = true 1736 emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1737 1738 query1 := "select * from music_user_map where id = 1" 1739 _, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true /* skipQueryPlanCache */) 1740 assertCacheSize(t, r.plans, 0) 1741 wantSQL := "select * from music_user_map where id = :id /* comment */" 1742 assert.Equal(t, wantSQL, logStats1.SQL) 1743 1744 _, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false /* skipQueryPlanCache */) 1745 assertCacheSize(t, r.plans, 1) 1746 assert.Equal(t, wantSQL, logStats2.SQL) 1747 1748 // Skip cache using directive 1749 r, _, _, _ = createExecutorEnv() 1750 r.normalize = true 1751 unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1752 1753 query1 = "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" 1754 getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1755 assertCacheSize(t, r.plans, 0) 1756 1757 query1 = "insert into user(id) values (1), (2)" 1758 getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1759 assertCacheSize(t, r.plans, 1) 1760 1761 // the target string will be resolved and become part of the plan cache key, which adds a new entry 1762 ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1763 getPlanCached(t, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1764 assertCacheSize(t, r.plans, 2) 1765 1766 // the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above 1767 ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1768 getPlanCached(t, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) 1769 assertCacheSize(t, r.plans, 2) 1770 } 1771 1772 func TestGetPlanNormalized(t *testing.T) { 1773 r, _, _, _ := createExecutorEnv() 1774 r.normalize = true 1775 emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1776 unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) 1777 1778 query1 := "select * from music_user_map where id = 1" 1779 query2 := "select * from music_user_map where id = 2" 1780 normalized := "select * from music_user_map where id = :id" 1781 1782 plan1, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment 1 */"), map[string]*querypb.BindVariable{}, false) 1783 plan2, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment 2 */"), map[string]*querypb.BindVariable{}, false) 1784 1785 assert.Equal(t, plan1, plan2) 1786 want := []string{ 1787 "@unknown:" + normalized, 1788 } 1789 assertCacheContains(t, r, want) 1790 1791 wantSQL := normalized + " /* comment 1 */" 1792 assert.Equal(t, wantSQL, logStats1.SQL) 1793 wantSQL = normalized + " /* comment 2 */" 1794 assert.Equal(t, wantSQL, logStats2.SQL) 1795 1796 plan3, logStats3 := getPlanCached(t, r, emptyvc, query2, makeComments(" /* comment 3 */"), map[string]*querypb.BindVariable{}, false) 1797 assert.Equal(t, plan1, plan3) 1798 wantSQL = normalized + " /* comment 3 */" 1799 assert.Equal(t, wantSQL, logStats3.SQL) 1800 1801 plan4, logStats4 := getPlanCached(t, r, emptyvc, normalized, makeComments(" /* comment 4 */"), map[string]*querypb.BindVariable{}, false) 1802 assert.Equal(t, plan1, plan4) 1803 wantSQL = normalized + " /* comment 4 */" 1804 assert.Equal(t, wantSQL, logStats4.SQL) 1805 1806 var logStats5 *logstats.LogStats 1807 plan3, logStats5 = getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment 5 */"), map[string]*querypb.BindVariable{}, false) 1808 assert.Equal(t, plan1, plan3) 1809 wantSQL = normalized + " /* comment 5 */" 1810 assert.Equal(t, wantSQL, logStats5.SQL) 1811 1812 plan4, _ = getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment 6 */"), map[string]*querypb.BindVariable{}, false) 1813 assert.Equal(t, plan1, plan4) 1814 want = []string{ 1815 KsTestUnsharded + "@unknown:" + normalized, 1816 "@unknown:" + normalized, 1817 } 1818 assertCacheContains(t, r, want) 1819 1820 _, _, err := r.getPlan(context.Background(), emptyvc, "syntax", makeComments(""), map[string]*querypb.BindVariable{}, nil, nil) 1821 assert.EqualError(t, err, "syntax error at position 7 near 'syntax'") 1822 assertCacheContains(t, r, want) 1823 } 1824 1825 func TestPassthroughDDL(t *testing.T) { 1826 executor, sbc1, sbc2, _ := createExecutorEnv() 1827 primarySession.TargetString = "TestExecutor" 1828 1829 alterDDL := "/* leading */ alter table passthrough_ddl add columne col bigint default 123 /* trailing */" 1830 _, err := executorExec(executor, alterDDL, nil) 1831 require.NoError(t, err) 1832 wantQueries := []*querypb.BoundQuery{{ 1833 Sql: alterDDL, 1834 BindVariables: map[string]*querypb.BindVariable{}, 1835 }} 1836 if !reflect.DeepEqual(sbc1.Queries, wantQueries) { 1837 t.Errorf("sbc1.Queries: %+v, want %+v\n", sbc1.Queries, wantQueries) 1838 } 1839 if !reflect.DeepEqual(sbc2.Queries, wantQueries) { 1840 t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc2.Queries, wantQueries) 1841 } 1842 sbc1.Queries = nil 1843 sbc2.Queries = nil 1844 1845 // Force the query to go to only one shard. Normalization doesn't make any difference. 1846 primarySession.TargetString = "TestExecutor/40-60" 1847 executor.normalize = true 1848 1849 _, err = executorExec(executor, alterDDL, nil) 1850 require.NoError(t, err) 1851 require.Nil(t, sbc1.Queries) 1852 if !reflect.DeepEqual(sbc2.Queries, wantQueries) { 1853 t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc2.Queries, wantQueries) 1854 } 1855 sbc2.Queries = nil 1856 primarySession.TargetString = "" 1857 1858 // Use range query 1859 primarySession.TargetString = "TestExecutor[-]" 1860 executor.normalize = true 1861 1862 _, err = executorExec(executor, alterDDL, nil) 1863 require.NoError(t, err) 1864 if !reflect.DeepEqual(sbc1.Queries, wantQueries) { 1865 t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc1.Queries, wantQueries) 1866 } 1867 if !reflect.DeepEqual(sbc2.Queries, wantQueries) { 1868 t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc2.Queries, wantQueries) 1869 } 1870 sbc2.Queries = nil 1871 primarySession.TargetString = "" 1872 } 1873 1874 func TestParseEmptyTargetSingleKeyspace(t *testing.T) { 1875 r, _, _, _ := createExecutorEnv() 1876 altVSchema := &vindexes.VSchema{ 1877 Keyspaces: map[string]*vindexes.KeyspaceSchema{ 1878 KsTestUnsharded: r.vschema.Keyspaces[KsTestUnsharded], 1879 }, 1880 } 1881 r.vschema = altVSchema 1882 1883 destKeyspace, destTabletType, _, _ := r.ParseDestinationTarget("") 1884 if destKeyspace != KsTestUnsharded || destTabletType != topodatapb.TabletType_PRIMARY { 1885 t.Errorf( 1886 "parseDestinationTarget(%s): got (%v, %v), want (%v, %v)", 1887 "@primary", 1888 destKeyspace, 1889 destTabletType, 1890 KsTestUnsharded, 1891 topodatapb.TabletType_PRIMARY, 1892 ) 1893 } 1894 } 1895 1896 func TestParseEmptyTargetMultiKeyspace(t *testing.T) { 1897 r, _, _, _ := createExecutorEnv() 1898 altVSchema := &vindexes.VSchema{ 1899 Keyspaces: map[string]*vindexes.KeyspaceSchema{ 1900 KsTestUnsharded: r.vschema.Keyspaces[KsTestUnsharded], 1901 KsTestSharded: r.vschema.Keyspaces[KsTestSharded], 1902 }, 1903 } 1904 r.vschema = altVSchema 1905 1906 destKeyspace, destTabletType, _, _ := r.ParseDestinationTarget("") 1907 if destKeyspace != "" || destTabletType != topodatapb.TabletType_PRIMARY { 1908 t.Errorf( 1909 "parseDestinationTarget(%s): got (%v, %v), want (%v, %v)", 1910 "@primary", 1911 destKeyspace, 1912 destTabletType, 1913 "", 1914 topodatapb.TabletType_PRIMARY, 1915 ) 1916 } 1917 } 1918 1919 func TestParseTargetSingleKeyspace(t *testing.T) { 1920 r, _, _, _ := createExecutorEnv() 1921 altVSchema := &vindexes.VSchema{ 1922 Keyspaces: map[string]*vindexes.KeyspaceSchema{ 1923 KsTestUnsharded: r.vschema.Keyspaces[KsTestUnsharded], 1924 }, 1925 } 1926 r.vschema = altVSchema 1927 1928 destKeyspace, destTabletType, _, _ := r.ParseDestinationTarget("@replica") 1929 if destKeyspace != KsTestUnsharded || destTabletType != topodatapb.TabletType_REPLICA { 1930 t.Errorf( 1931 "parseDestinationTarget(%s): got (%v, %v), want (%v, %v)", 1932 "@replica", 1933 destKeyspace, 1934 destTabletType, 1935 KsTestUnsharded, 1936 topodatapb.TabletType_REPLICA, 1937 ) 1938 } 1939 } 1940 1941 func TestDebugVSchema(t *testing.T) { 1942 resp := httptest.NewRecorder() 1943 req, _ := http.NewRequest("GET", "/debug/vschema", nil) 1944 1945 executor, _, _, _ := createExecutorEnv() 1946 executor.ServeHTTP(resp, req) 1947 v := make(map[string]any) 1948 if err := json.Unmarshal(resp.Body.Bytes(), &v); err != nil { 1949 t.Fatalf("Unmarshal on %s failed: %v", resp.Body.String(), err) 1950 } 1951 if _, ok := v["routing_rules"]; !ok { 1952 t.Errorf("routing rules missing: %v", resp.Body.String()) 1953 } 1954 if _, ok := v["keyspaces"]; !ok { 1955 t.Errorf("keyspaces missing: %v", resp.Body.String()) 1956 } 1957 } 1958 1959 func TestExecutorMaxPayloadSizeExceeded(t *testing.T) { 1960 saveMax := maxPayloadSize 1961 saveWarn := warnPayloadSize 1962 maxPayloadSize = 10 1963 warnPayloadSize = 5 1964 defer func() { 1965 maxPayloadSize = saveMax 1966 warnPayloadSize = saveWarn 1967 }() 1968 1969 executor, _, _, _ := createExecutorEnv() 1970 session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) 1971 warningCount := warnings.Counts()["WarnPayloadSizeExceeded"] 1972 testMaxPayloadSizeExceeded := []string{ 1973 "select * from main1", 1974 "insert into main1(id) values (1), (2)", 1975 "update main1 set id=1", 1976 "delete from main1 where id=1", 1977 } 1978 for _, query := range testMaxPayloadSizeExceeded { 1979 _, err := executor.Execute(context.Background(), "TestExecutorMaxPayloadSizeExceeded", session, query, nil) 1980 require.NotNil(t, err) 1981 assert.EqualError(t, err, "query payload size above threshold") 1982 } 1983 assert.Equal(t, warningCount, warnings.Counts()["WarnPayloadSizeExceeded"], "warnings count") 1984 1985 testMaxPayloadSizeOverride := []string{ 1986 "select /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ * from main1", 1987 "insert /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ into main1(id) values (1), (2)", 1988 "update /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ main1 set id=1", 1989 "delete /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ from main1 where id=1", 1990 } 1991 for _, query := range testMaxPayloadSizeOverride { 1992 _, err := executor.Execute(context.Background(), "TestExecutorMaxPayloadSizeWithOverride", session, query, nil) 1993 assert.Equal(t, nil, err, "err should be nil") 1994 } 1995 assert.Equal(t, warningCount, warnings.Counts()["WarnPayloadSizeExceeded"], "warnings count") 1996 1997 maxPayloadSize = 1000 1998 for _, query := range testMaxPayloadSizeExceeded { 1999 _, err := executor.Execute(context.Background(), "TestExecutorMaxPayloadSizeExceeded", session, query, nil) 2000 assert.Equal(t, nil, err, "err should be nil") 2001 } 2002 assert.Equal(t, warningCount+4, warnings.Counts()["WarnPayloadSizeExceeded"], "warnings count") 2003 } 2004 2005 func TestOlapSelectDatabase(t *testing.T) { 2006 executor, _, _, _ := createExecutorEnv() 2007 executor.normalize = true 2008 2009 session := &vtgatepb.Session{Autocommit: true} 2010 2011 sql := `select database()` 2012 cbInvoked := false 2013 cb := func(r *sqltypes.Result) error { 2014 cbInvoked = true 2015 return nil 2016 } 2017 err := executor.StreamExecute(context.Background(), "TestExecute", NewSafeSession(session), sql, nil, cb) 2018 assert.NoError(t, err) 2019 assert.True(t, cbInvoked) 2020 } 2021 2022 func TestExecutorClearsWarnings(t *testing.T) { 2023 executor, _, _, _ := createExecutorEnv() 2024 session := NewSafeSession(&vtgatepb.Session{ 2025 Warnings: []*querypb.QueryWarning{{Code: 234, Message: "oh noes"}}, 2026 }) 2027 _, err := executor.Execute(context.Background(), "TestExecute", session, "select 42", nil) 2028 require.NoError(t, err) 2029 require.Empty(t, session.Warnings) 2030 } 2031 2032 func TestExecutorOtherRead(t *testing.T) { 2033 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 2034 2035 type cnts struct { 2036 Sbc1Cnt int64 2037 Sbc2Cnt int64 2038 SbcLookupCnt int64 2039 } 2040 2041 tcs := []struct { 2042 targetStr string 2043 2044 hasNoKeyspaceErr bool 2045 hasDestinationShardErr bool 2046 wantCnts cnts 2047 }{ 2048 { 2049 targetStr: "", 2050 hasNoKeyspaceErr: true, 2051 }, 2052 { 2053 targetStr: "TestExecutor[-]", 2054 hasDestinationShardErr: true, 2055 }, 2056 { 2057 targetStr: KsTestUnsharded, 2058 wantCnts: cnts{ 2059 Sbc1Cnt: 0, 2060 Sbc2Cnt: 0, 2061 SbcLookupCnt: 1, 2062 }, 2063 }, 2064 { 2065 targetStr: "TestExecutor", 2066 wantCnts: cnts{ 2067 Sbc1Cnt: 1, 2068 Sbc2Cnt: 0, 2069 SbcLookupCnt: 0, 2070 }, 2071 }, 2072 } 2073 2074 stmts := []string{ 2075 "analyze table t1", 2076 "describe select * from t1", 2077 "explain select * from t1", 2078 "do 1", 2079 } 2080 2081 for _, stmt := range stmts { 2082 for _, tc := range tcs { 2083 t.Run(stmt+tc.targetStr, func(t *testing.T) { 2084 sbc1.ExecCount.Set(0) 2085 sbc2.ExecCount.Set(0) 2086 sbclookup.ExecCount.Set(0) 2087 2088 _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) 2089 if tc.hasNoKeyspaceErr { 2090 assert.EqualError(t, err, errNoKeyspace.Error()) 2091 } else if tc.hasDestinationShardErr { 2092 assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt) 2093 } else { 2094 assert.NoError(t, err) 2095 } 2096 2097 utils.MustMatch(t, tc.wantCnts, cnts{ 2098 Sbc1Cnt: sbc1.ExecCount.Get(), 2099 Sbc2Cnt: sbc2.ExecCount.Get(), 2100 SbcLookupCnt: sbclookup.ExecCount.Get(), 2101 }, "count did not match") 2102 }) 2103 } 2104 } 2105 } 2106 2107 func TestExecutorVExplain(t *testing.T) { 2108 executor, _, _, _ := createExecutorEnv() 2109 executor.normalize = true 2110 logChan := QueryLogger.Subscribe("Test") 2111 defer QueryLogger.Unsubscribe(logChan) 2112 2113 bindVars := map[string]*querypb.BindVariable{} 2114 result, err := executorExec(executor, "vexplain plan select * from user", bindVars) 2115 require.NoError(t, err) 2116 2117 require.Equal(t, 2118 `[[VARCHAR("{\n\t\"OperatorType\": \"Route\",\n\t\"Variant\": \"Scatter\",\n\t\"Keyspace\": {\n\t\t\"Name\": \"TestExecutor\",\n\t\t\"Sharded\": true\n\t},\n\t\"FieldQuery\": \"select * from `+"`user`"+` where 1 != 1\",\n\t\"Query\": \"select * from `+"`user`"+`\",\n\t\"Table\": \"`+"`user`"+`\"\n}")]]`, 2119 fmt.Sprintf("%v", result.Rows)) 2120 2121 result, err = executorExec(executor, "vexplain plan select 42", bindVars) 2122 require.NoError(t, err) 2123 expected := `[[VARCHAR("{\n\t\"OperatorType\": \"Projection\",\n\t\"Expressions\": [\n\t\t\"INT64(42) as 42\"\n\t],\n\t\"Inputs\": [\n\t\t{\n\t\t\t\"OperatorType\": \"SingleRow\"\n\t\t}\n\t]\n}")]]` 2124 require.Equal(t, expected, fmt.Sprintf("%v", result.Rows)) 2125 } 2126 2127 func TestExecutorOtherAdmin(t *testing.T) { 2128 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 2129 2130 type cnts struct { 2131 Sbc1Cnt int64 2132 Sbc2Cnt int64 2133 SbcLookupCnt int64 2134 } 2135 2136 tcs := []struct { 2137 targetStr string 2138 2139 hasNoKeyspaceErr bool 2140 hasDestinationShardErr bool 2141 wantCnts cnts 2142 }{ 2143 { 2144 targetStr: "", 2145 hasNoKeyspaceErr: true, 2146 }, 2147 { 2148 targetStr: "TestExecutor[-]", 2149 hasDestinationShardErr: true, 2150 }, 2151 { 2152 targetStr: KsTestUnsharded, 2153 wantCnts: cnts{ 2154 Sbc1Cnt: 0, 2155 Sbc2Cnt: 0, 2156 SbcLookupCnt: 1, 2157 }, 2158 }, 2159 { 2160 targetStr: "TestExecutor", 2161 wantCnts: cnts{ 2162 Sbc1Cnt: 1, 2163 Sbc2Cnt: 0, 2164 SbcLookupCnt: 0, 2165 }, 2166 }, 2167 } 2168 2169 stmts := []string{ 2170 "repair table t1", 2171 "optimize table t1", 2172 } 2173 2174 for _, stmt := range stmts { 2175 for _, tc := range tcs { 2176 sbc1.ExecCount.Set(0) 2177 sbc2.ExecCount.Set(0) 2178 sbclookup.ExecCount.Set(0) 2179 2180 _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) 2181 if tc.hasNoKeyspaceErr { 2182 assert.Error(t, err, errNoKeyspace) 2183 } else if tc.hasDestinationShardErr { 2184 assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt) 2185 } else { 2186 assert.NoError(t, err) 2187 } 2188 2189 diff := cmp.Diff(tc.wantCnts, cnts{ 2190 Sbc1Cnt: sbc1.ExecCount.Get(), 2191 Sbc2Cnt: sbc2.ExecCount.Get(), 2192 SbcLookupCnt: sbclookup.ExecCount.Get(), 2193 }) 2194 if diff != "" { 2195 t.Errorf("stmt: %s\ntc: %+v\n-want,+got:\n%s", stmt, tc, diff) 2196 } 2197 } 2198 } 2199 } 2200 2201 func TestExecutorSavepointInTx(t *testing.T) { 2202 executor, sbc1, sbc2, _ := createExecutorEnv() 2203 logChan := QueryLogger.Subscribe("TestExecutorSavepoint") 2204 defer QueryLogger.Unsubscribe(logChan) 2205 2206 session := NewSafeSession(&vtgatepb.Session{Autocommit: false, TargetString: "@primary"}) 2207 _, err := exec(executor, session, "savepoint a") 2208 require.NoError(t, err) 2209 _, err = exec(executor, session, "rollback to a") 2210 require.NoError(t, err) 2211 _, err = exec(executor, session, "release savepoint a") 2212 require.NoError(t, err) 2213 _, err = exec(executor, session, "select id from user where id = 1") 2214 require.NoError(t, err) 2215 _, err = exec(executor, session, "savepoint b") 2216 require.NoError(t, err) 2217 _, err = exec(executor, session, "rollback to b") 2218 require.NoError(t, err) 2219 _, err = exec(executor, session, "release savepoint b") 2220 require.NoError(t, err) 2221 _, err = exec(executor, session, "select id from user where id = 3") 2222 require.NoError(t, err) 2223 _, err = exec(executor, session, "rollback") 2224 require.NoError(t, err) 2225 sbc1WantQueries := []*querypb.BoundQuery{{ 2226 Sql: "savepoint a", 2227 BindVariables: map[string]*querypb.BindVariable{}, 2228 }, { 2229 Sql: "rollback to a", 2230 BindVariables: map[string]*querypb.BindVariable{}, 2231 }, { 2232 Sql: "release savepoint a", 2233 BindVariables: map[string]*querypb.BindVariable{}, 2234 }, { 2235 Sql: "select id from `user` where id = 1", 2236 BindVariables: map[string]*querypb.BindVariable{}, 2237 }, { 2238 Sql: "savepoint b", 2239 BindVariables: map[string]*querypb.BindVariable{}, 2240 }, { 2241 Sql: "rollback to b", 2242 BindVariables: map[string]*querypb.BindVariable{}, 2243 }, { 2244 Sql: "release savepoint b", 2245 BindVariables: map[string]*querypb.BindVariable{}, 2246 }} 2247 2248 sbc2WantQueries := []*querypb.BoundQuery{{ 2249 Sql: "savepoint a", 2250 BindVariables: map[string]*querypb.BindVariable{}, 2251 }, { 2252 Sql: "rollback to a", 2253 BindVariables: map[string]*querypb.BindVariable{}, 2254 }, { 2255 Sql: "release savepoint a", 2256 BindVariables: map[string]*querypb.BindVariable{}, 2257 }, { 2258 Sql: "savepoint b", 2259 BindVariables: map[string]*querypb.BindVariable{}, 2260 }, { 2261 Sql: "rollback to b", 2262 BindVariables: map[string]*querypb.BindVariable{}, 2263 }, { 2264 Sql: "release savepoint b", 2265 BindVariables: map[string]*querypb.BindVariable{}, 2266 }, { 2267 Sql: "select id from `user` where id = 3", 2268 BindVariables: map[string]*querypb.BindVariable{}, 2269 }} 2270 utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "") 2271 utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "") 2272 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint a", 0) 2273 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to a", 0) 2274 testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint a", 0) 2275 testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 1", 1) 2276 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint b", 1) 2277 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to b", 1) 2278 testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint b", 1) 2279 testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 3", 1) 2280 testQueryLog(t, logChan, "TestExecute", "ROLLBACK", "rollback", 2) 2281 } 2282 2283 func TestExecutorSavepointInTxWithReservedConn(t *testing.T) { 2284 executor, sbc1, sbc2, _ := createExecutorEnv() 2285 logChan := QueryLogger.Subscribe("TestExecutorSavepoint") 2286 defer QueryLogger.Unsubscribe(logChan) 2287 2288 session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "TestExecutor", EnableSystemSettings: true}) 2289 sbc1.SetResults([]*sqltypes.Result{ 2290 sqltypes.MakeTestResult(sqltypes.MakeTestFields("orig|new", "varchar|varchar"), "a|"), 2291 }) 2292 _, err := exec(executor, session, "set sql_mode = ''") 2293 require.NoError(t, err) 2294 2295 _, err = exec(executor, session, "begin") 2296 require.NoError(t, err) 2297 _, err = exec(executor, session, "savepoint a") 2298 require.NoError(t, err) 2299 _, err = exec(executor, session, "select id from user where id = 1") 2300 require.NoError(t, err) 2301 _, err = exec(executor, session, "savepoint b") 2302 require.NoError(t, err) 2303 _, err = exec(executor, session, "release savepoint a") 2304 require.NoError(t, err) 2305 _, err = exec(executor, session, "select id from user where id = 3") 2306 require.NoError(t, err) 2307 _, err = exec(executor, session, "commit") 2308 require.NoError(t, err) 2309 emptyBV := map[string]*querypb.BindVariable{} 2310 2311 sbc1WantQueries := []*querypb.BoundQuery{{ 2312 Sql: "select @@sql_mode orig, '' new", BindVariables: emptyBV, 2313 }, { 2314 Sql: "set sql_mode = ''", BindVariables: emptyBV, 2315 }, { 2316 Sql: "savepoint a", BindVariables: emptyBV, 2317 }, { 2318 Sql: "select id from `user` where id = 1", BindVariables: emptyBV, 2319 }, { 2320 Sql: "savepoint b", BindVariables: emptyBV, 2321 }, { 2322 Sql: "release savepoint a", BindVariables: emptyBV, 2323 }} 2324 2325 sbc2WantQueries := []*querypb.BoundQuery{{ 2326 Sql: "set sql_mode = ''", BindVariables: emptyBV, 2327 }, { 2328 Sql: "savepoint a", BindVariables: emptyBV, 2329 }, { 2330 Sql: "savepoint b", BindVariables: emptyBV, 2331 }, { 2332 Sql: "release savepoint a", BindVariables: emptyBV, 2333 }, { 2334 Sql: "select id from `user` where id = 3", BindVariables: emptyBV, 2335 }} 2336 2337 utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "") 2338 utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "") 2339 testQueryLog(t, logChan, "TestExecute", "SET", "set @@sql_mode = ''", 1) 2340 testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0) 2341 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint a", 0) 2342 testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 1", 1) 2343 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint b", 1) 2344 testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint a", 1) 2345 testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 3", 1) 2346 testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 2) 2347 } 2348 2349 func TestExecutorSavepointWithoutTx(t *testing.T) { 2350 executor, sbc1, sbc2, _ := createExecutorEnv() 2351 logChan := QueryLogger.Subscribe("TestExecutorSavepoint") 2352 defer QueryLogger.Unsubscribe(logChan) 2353 2354 session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary", InTransaction: false}) 2355 _, err := exec(executor, session, "savepoint a") 2356 require.NoError(t, err) 2357 _, err = exec(executor, session, "rollback to a") 2358 require.Error(t, err) 2359 _, err = exec(executor, session, "release savepoint a") 2360 require.Error(t, err) 2361 _, err = exec(executor, session, "select id from user where id = 1") 2362 require.NoError(t, err) 2363 _, err = exec(executor, session, "savepoint b") 2364 require.NoError(t, err) 2365 _, err = exec(executor, session, "rollback to b") 2366 require.Error(t, err) 2367 _, err = exec(executor, session, "release savepoint b") 2368 require.Error(t, err) 2369 _, err = exec(executor, session, "select id from user where id = 3") 2370 require.NoError(t, err) 2371 sbc1WantQueries := []*querypb.BoundQuery{{ 2372 Sql: "select id from `user` where id = 1", 2373 BindVariables: map[string]*querypb.BindVariable{}, 2374 }} 2375 2376 sbc2WantQueries := []*querypb.BoundQuery{{ 2377 Sql: "select id from `user` where id = 3", 2378 BindVariables: map[string]*querypb.BindVariable{}, 2379 }} 2380 utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "") 2381 utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "") 2382 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint a", 0) 2383 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to a", 0) 2384 testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint a", 0) 2385 testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 1", 1) 2386 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint b", 0) 2387 testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to b", 0) 2388 testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint b", 0) 2389 testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 3", 1) 2390 } 2391 2392 func TestExecutorCallProc(t *testing.T) { 2393 executor, sbc1, sbc2, sbcUnsharded := createExecutorEnv() 2394 2395 type cnts struct { 2396 Sbc1Cnt int64 2397 Sbc2Cnt int64 2398 SbcUnsharded int64 2399 } 2400 2401 tcs := []struct { 2402 name, targetStr string 2403 2404 hasNoKeyspaceErr bool 2405 unshardedOnlyErr bool 2406 wantCnts cnts 2407 }{{ 2408 name: "simple call with no keyspace set", 2409 targetStr: "", 2410 hasNoKeyspaceErr: true, 2411 }, { 2412 name: "keyrange targeted keyspace", 2413 targetStr: "TestExecutor[-]", 2414 wantCnts: cnts{ 2415 Sbc1Cnt: 1, 2416 Sbc2Cnt: 1, 2417 SbcUnsharded: 0, 2418 }, 2419 }, { 2420 name: "unsharded call proc", 2421 targetStr: KsTestUnsharded, 2422 wantCnts: cnts{ 2423 Sbc1Cnt: 0, 2424 Sbc2Cnt: 0, 2425 SbcUnsharded: 1, 2426 }, 2427 }, { 2428 name: "should fail with sharded call proc", 2429 targetStr: "TestExecutor", 2430 unshardedOnlyErr: true, 2431 }} 2432 2433 for _, tc := range tcs { 2434 t.Run(tc.name, func(t *testing.T) { 2435 sbc1.ExecCount.Set(0) 2436 sbc2.ExecCount.Set(0) 2437 sbcUnsharded.ExecCount.Set(0) 2438 2439 _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), "CALL proc()", nil) 2440 if tc.hasNoKeyspaceErr { 2441 assert.EqualError(t, err, errNoKeyspace.Error()) 2442 } else if tc.unshardedOnlyErr { 2443 require.EqualError(t, err, "CALL is not supported for sharded keyspace") 2444 } else { 2445 assert.NoError(t, err) 2446 } 2447 2448 utils.MustMatch(t, tc.wantCnts, cnts{ 2449 Sbc1Cnt: sbc1.ExecCount.Get(), 2450 Sbc2Cnt: sbc2.ExecCount.Get(), 2451 SbcUnsharded: sbcUnsharded.ExecCount.Get(), 2452 }, "count did not match") 2453 }) 2454 } 2455 } 2456 2457 func TestExecutorTempTable(t *testing.T) { 2458 executor, _, _, sbcUnsharded := createExecutorEnv() 2459 executor.warnShardedOnly = true 2460 creatQuery := "create temporary table temp_t(id bigint primary key)" 2461 session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) 2462 ctx := context.Background() 2463 _, err := executor.Execute(ctx, "TestExecutorTempTable", session, creatQuery, nil) 2464 require.NoError(t, err) 2465 assert.EqualValues(t, 1, sbcUnsharded.ExecCount.Get()) 2466 assert.NotEmpty(t, session.Warnings) 2467 2468 before := executor.plans.Len() 2469 2470 _, err = executor.Execute(ctx, "TestExecutorTempTable", session, "select * from temp_t", nil) 2471 require.NoError(t, err) 2472 2473 assert.Equal(t, before, executor.plans.Len()) 2474 } 2475 2476 func TestExecutorShowVitessMigrations(t *testing.T) { 2477 executor, sbc1, sbc2, _ := createExecutorEnv() 2478 showQuery := "show vitess_migrations" 2479 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 2480 ctx := context.Background() 2481 _, err := executor.Execute(ctx, "", session, showQuery, nil) 2482 require.NoError(t, err) 2483 assert.Contains(t, sbc1.StringQueries(), "SELECT * FROM _vt.schema_migrations") 2484 assert.Contains(t, sbc2.StringQueries(), "SELECT * FROM _vt.schema_migrations") 2485 } 2486 2487 func TestExecutorDescHash(t *testing.T) { 2488 executor, _, _, _ := createExecutorEnv() 2489 showQuery := "desc hash_index" 2490 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 2491 ctx := context.Background() 2492 _, err := executor.Execute(ctx, "", session, showQuery, nil) 2493 require.NoError(t, err) 2494 } 2495 2496 func TestExecutorVExplainQueries(t *testing.T) { 2497 executor, _, _, sbclookup := createExecutorEnv() 2498 session := NewAutocommitSession(&vtgatepb.Session{}) 2499 2500 sbclookup.SetResults([]*sqltypes.Result{ 2501 sqltypes.MakeTestResult(sqltypes.MakeTestFields("name|user_id", "varchar|int64"), "apa|1", "apa|2"), 2502 }) 2503 qr, err := executor.Execute(ctx, "TestExecutorVExplainQueries", session, "vexplain queries select * from user where name = 'apa'", nil) 2504 require.NoError(t, err) 2505 txt := fmt.Sprintf("%v\n", qr.Rows) 2506 lookupQuery := "select `name`, user_id from name_user_map where `name` in" 2507 require.Contains(t, txt, lookupQuery) 2508 2509 // Test the streaming side as well 2510 var results []sqltypes.Row 2511 session = NewAutocommitSession(&vtgatepb.Session{}) 2512 err = executor.StreamExecute(ctx, "TestExecutorVExplainQueries", session, "vexplain queries select * from user where name = 'apa'", nil, func(result *sqltypes.Result) error { 2513 results = append(results, result.Rows...) 2514 return nil 2515 }) 2516 require.NoError(t, err) 2517 txt = fmt.Sprintf("%v\n", results) 2518 require.Contains(t, txt, lookupQuery) 2519 } 2520 2521 func TestExecutorStartTxnStmt(t *testing.T) { 2522 executor, _, _, _ := createExecutorEnv() 2523 session := NewAutocommitSession(&vtgatepb.Session{}) 2524 2525 tcases := []struct { 2526 beginSQL string 2527 expTxAccessMode []querypb.ExecuteOptions_TransactionAccessMode 2528 }{{ 2529 beginSQL: "begin", 2530 }, { 2531 beginSQL: "start transaction", 2532 }, { 2533 beginSQL: "start transaction with consistent snapshot", 2534 expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_CONSISTENT_SNAPSHOT}, 2535 }, { 2536 beginSQL: "start transaction read only", 2537 expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_ONLY}, 2538 }, { 2539 beginSQL: "start transaction read write", 2540 expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_WRITE}, 2541 }, { 2542 beginSQL: "start transaction with consistent snapshot, read only", 2543 expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_CONSISTENT_SNAPSHOT, querypb.ExecuteOptions_READ_ONLY}, 2544 }, { 2545 beginSQL: "start transaction with consistent snapshot, read write", 2546 expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_CONSISTENT_SNAPSHOT, querypb.ExecuteOptions_READ_WRITE}, 2547 }, { 2548 beginSQL: "start transaction read only, with consistent snapshot", 2549 expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_ONLY, querypb.ExecuteOptions_CONSISTENT_SNAPSHOT}, 2550 }} 2551 2552 for _, tcase := range tcases { 2553 t.Run(tcase.beginSQL, func(t *testing.T) { 2554 _, err := executor.Execute(ctx, "TestExecutorStartTxnStmt", session, tcase.beginSQL, nil) 2555 require.NoError(t, err) 2556 2557 assert.Equal(t, tcase.expTxAccessMode, session.GetOrCreateOptions().TransactionAccessMode) 2558 2559 _, err = executor.Execute(ctx, "TestExecutorStartTxnStmt", session, "rollback", nil) 2560 require.NoError(t, err) 2561 2562 }) 2563 } 2564 } 2565 2566 func exec(executor *Executor, session *SafeSession, sql string) (*sqltypes.Result, error) { 2567 return executor.Execute(context.Background(), "TestExecute", session, sql, nil) 2568 } 2569 2570 func makeComments(text string) sqlparser.MarginComments { 2571 return sqlparser.MarginComments{Trailing: text} 2572 }