vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/query_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 tabletserver 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "math/rand" 24 "strings" 25 "testing" 26 27 "vitess.io/vitess/go/vt/sidecardb" 28 29 "vitess.io/vitess/go/vt/vttablet/tabletserver/tx" 30 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 34 "vitess.io/vitess/go/mysql" 35 "vitess.io/vitess/go/mysql/fakesqldb" 36 "vitess.io/vitess/go/sqltypes" 37 "vitess.io/vitess/go/vt/callerid" 38 "vitess.io/vitess/go/vt/callinfo" 39 "vitess.io/vitess/go/vt/callinfo/fakecallinfo" 40 "vitess.io/vitess/go/vt/tableacl" 41 "vitess.io/vitess/go/vt/tableacl/simpleacl" 42 "vitess.io/vitess/go/vt/topo/memorytopo" 43 "vitess.io/vitess/go/vt/vterrors" 44 "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" 45 "vitess.io/vitess/go/vt/vttablet/tabletserver/rules" 46 "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" 47 48 querypb "vitess.io/vitess/go/vt/proto/query" 49 tableaclpb "vitess.io/vitess/go/vt/proto/tableacl" 50 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 51 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 52 ) 53 54 func TestQueryExecutorPlans(t *testing.T) { 55 type dbResponse struct { 56 query string 57 result *sqltypes.Result 58 } 59 60 dmlResult := &sqltypes.Result{ 61 RowsAffected: 1, 62 } 63 fields := sqltypes.MakeTestFields("a|b", "int64|varchar") 64 selectResult := sqltypes.MakeTestResult(fields, "1|aaa") 65 emptyResult := &sqltypes.Result{} 66 67 // The queries are run both in and outside a transaction. 68 testcases := []struct { 69 // input is the input query. 70 input string 71 // passThrough specifies if planbuilder.PassthroughDML must be set. 72 passThrough bool 73 // dbResponses specifes the list of queries and responses to add to the fake db. 74 dbResponses []dbResponse 75 // resultWant is the result we want. 76 resultWant *sqltypes.Result 77 // planWant is the PlanType we want to see built. 78 planWant string 79 // logWant is the log of queries we expect to be executed. 80 logWant string 81 // inTxWant is the query log we expect if we're in a transation. 82 // If empty, then we should expect the same as logWant. 83 inTxWant string 84 }{{ 85 input: "select * from t", 86 dbResponses: []dbResponse{{ 87 query: "select * from t limit 10001", 88 result: selectResult, 89 }}, 90 resultWant: selectResult, 91 planWant: "Select", 92 logWant: "select * from t limit 10001", 93 inTxWant: "select * from t limit 10001", 94 }, { 95 input: "select * from t limit 1", 96 dbResponses: []dbResponse{{ 97 query: "select * from t limit 1", 98 result: selectResult, 99 }}, 100 resultWant: selectResult, 101 planWant: "Select", 102 logWant: "select * from t limit 1", 103 inTxWant: "select * from t limit 1", 104 }, { 105 input: "show engines", 106 dbResponses: []dbResponse{{ 107 query: "show engines", 108 result: dmlResult, 109 }}, 110 resultWant: dmlResult, 111 planWant: "Show", 112 logWant: "show engines", 113 }, { 114 input: "repair t", 115 dbResponses: []dbResponse{{ 116 query: "repair t", 117 result: dmlResult, 118 }}, 119 resultWant: dmlResult, 120 planWant: "OtherAdmin", 121 logWant: "repair t", 122 }, { 123 input: "insert into test_table(a) values(1)", 124 dbResponses: []dbResponse{{ 125 query: "insert into test_table(a) values (1)", 126 result: dmlResult, 127 }}, 128 resultWant: dmlResult, 129 planWant: "Insert", 130 logWant: "insert into test_table(a) values (1)", 131 }, { 132 input: "replace into test_table(a) values(1)", 133 dbResponses: []dbResponse{{ 134 query: "replace into test_table(a) values (1)", 135 result: dmlResult, 136 }}, 137 resultWant: dmlResult, 138 planWant: "Insert", 139 logWant: "replace into test_table(a) values (1)", 140 }, { 141 input: "update test_table set a=1", 142 dbResponses: []dbResponse{{ 143 query: "update test_table set a = 1 limit 10001", 144 result: dmlResult, 145 }}, 146 resultWant: dmlResult, 147 planWant: "UpdateLimit", 148 // The UpdateLimit query will not use autocommit because 149 // it needs to roll back on failure. 150 logWant: "begin; update test_table set a = 1 limit 10001; commit", 151 inTxWant: "update test_table set a = 1 limit 10001", 152 }, { 153 input: "update test_table set a=1", 154 passThrough: true, 155 dbResponses: []dbResponse{{ 156 query: "update test_table set a = 1", 157 result: dmlResult, 158 }}, 159 resultWant: dmlResult, 160 planWant: "Update", 161 logWant: "update test_table set a = 1", 162 }, { 163 input: "delete from test_table", 164 dbResponses: []dbResponse{{ 165 query: "delete from test_table limit 10001", 166 result: dmlResult, 167 }}, 168 resultWant: dmlResult, 169 planWant: "DeleteLimit", 170 // The DeleteLimit query will not use autocommit because 171 // it needs to roll back on failure. 172 logWant: "begin; delete from test_table limit 10001; commit", 173 inTxWant: "delete from test_table limit 10001", 174 }, { 175 input: "delete from test_table", 176 passThrough: true, 177 dbResponses: []dbResponse{{ 178 query: "delete from test_table", 179 result: dmlResult, 180 }}, 181 resultWant: dmlResult, 182 planWant: "Delete", 183 logWant: "delete from test_table", 184 }, { 185 input: "alter table test_table add zipcode int", 186 dbResponses: []dbResponse{{ 187 query: "alter table test_table add column zipcode int", 188 result: dmlResult, 189 }}, 190 resultWant: dmlResult, 191 planWant: "DDL", 192 logWant: "alter table test_table add column zipcode int", 193 }, { 194 input: "savepoint a", 195 dbResponses: []dbResponse{{ 196 query: "savepoint a", 197 result: emptyResult, 198 }}, 199 resultWant: emptyResult, 200 planWant: "Savepoint", 201 logWant: "savepoint a", 202 inTxWant: "savepoint a", 203 }, { 204 input: "create index a on user(id)", 205 dbResponses: []dbResponse{{ 206 query: "alter table `user` add index a (id)", 207 result: emptyResult, 208 }}, 209 resultWant: emptyResult, 210 planWant: "DDL", 211 logWant: "alter table `user` add index a (id)", 212 inTxWant: "alter table `user` add index a (id)", 213 }, { 214 input: "create index a on user(id1 + id2)", 215 dbResponses: []dbResponse{{ 216 query: "create index a on user(id1 + id2)", 217 result: emptyResult, 218 }}, 219 resultWant: emptyResult, 220 planWant: "DDL", 221 logWant: "create index a on user(id1 + id2)", 222 inTxWant: "create index a on user(id1 + id2)", 223 }, { 224 input: "ROLLBACK work to SAVEPOINT a", 225 dbResponses: []dbResponse{{ 226 query: "ROLLBACK work to SAVEPOINT a", 227 result: emptyResult, 228 }}, 229 resultWant: emptyResult, 230 planWant: "RollbackSavepoint", 231 logWant: "ROLLBACK work to SAVEPOINT a", 232 inTxWant: "ROLLBACK work to SAVEPOINT a", 233 }, { 234 input: "RELEASE savepoint a", 235 dbResponses: []dbResponse{{ 236 query: "RELEASE savepoint a", 237 result: emptyResult, 238 }}, 239 resultWant: emptyResult, 240 planWant: "Release", 241 logWant: "RELEASE savepoint a", 242 inTxWant: "RELEASE savepoint a", 243 }, { 244 input: "show create database db_name", 245 dbResponses: []dbResponse{{ 246 query: "show create database ks", 247 result: emptyResult, 248 }}, 249 resultWant: emptyResult, 250 planWant: "Show", 251 logWant: "show create database ks", 252 }, { 253 input: "show create database mysql", 254 dbResponses: []dbResponse{{ 255 query: "show create database mysql", 256 result: emptyResult, 257 }}, 258 resultWant: emptyResult, 259 planWant: "Show", 260 logWant: "show create database mysql", 261 }, { 262 input: "show create table mysql.user", 263 dbResponses: []dbResponse{{ 264 query: "show create table mysql.`user`", 265 result: emptyResult, 266 }}, 267 resultWant: emptyResult, 268 planWant: "Show", 269 logWant: "show create table mysql.`user`", 270 }} 271 for _, tcase := range testcases { 272 t.Run(tcase.input, func(t *testing.T) { 273 db := setUpQueryExecutorTest(t) 274 defer db.Close() 275 for _, dbr := range tcase.dbResponses { 276 db.AddQuery(dbr.query, dbr.result) 277 } 278 ctx := context.Background() 279 tsv := newTestTabletServer(ctx, noFlags, db) 280 tsv.config.DB.DBName = "ks" 281 defer tsv.StopService() 282 283 tsv.SetPassthroughDMLs(tcase.passThrough) 284 285 // Test outside a transaction. 286 qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) 287 got, err := qre.Execute() 288 require.NoError(t, err, tcase.input) 289 assert.Equal(t, tcase.resultWant, got, tcase.input) 290 assert.Equal(t, tcase.planWant, qre.logStats.PlanType, tcase.input) 291 assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) 292 293 // Wait for the existing query to be processed by the cache 294 tsv.QueryPlanCacheWait() 295 296 // Test inside a transaction. 297 target := tsv.sm.Target() 298 state, err := tsv.Begin(ctx, target, nil) 299 require.NoError(t, err) 300 require.NotNil(t, state.TabletAlias, "alias should not be nil") 301 assert.Equal(t, tsv.alias, state.TabletAlias, "Wrong alias returned by Begin") 302 defer tsv.Commit(ctx, target, state.TransactionID) 303 304 qre = newTestQueryExecutor(ctx, tsv, tcase.input, state.TransactionID) 305 got, err = qre.Execute() 306 require.NoError(t, err, tcase.input) 307 assert.Equal(t, tcase.resultWant, got, "in tx: %v", tcase.input) 308 assert.Equal(t, tcase.planWant, qre.logStats.PlanType, "in tx: %v", tcase.input) 309 want := tcase.logWant 310 if tcase.inTxWant != "" { 311 want = tcase.inTxWant 312 } 313 assert.Equal(t, want, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) 314 }) 315 } 316 } 317 318 func TestQueryExecutorQueryAnnotation(t *testing.T) { 319 type dbResponse struct { 320 query string 321 result *sqltypes.Result 322 } 323 324 fields := sqltypes.MakeTestFields("a|b", "int64|varchar") 325 selectResult := sqltypes.MakeTestResult(fields, "1|aaa") 326 327 testcases := []struct { 328 // input is the input query. 329 input string 330 // passThrough specifies if planbuilder.PassthroughDML must be set. 331 passThrough bool 332 // dbResponses specifes the list of queries and responses to add to the fake db. 333 dbResponses []dbResponse 334 // resultWant is the result we want. 335 resultWant *sqltypes.Result 336 // planWant is the PlanType we want to see built. 337 planWant string 338 // logWant is the log of queries we expect to be executed. 339 logWant string 340 // If empty, then we should expect the same as logWant. 341 inTxWant string 342 }{{ 343 input: "select * from t", 344 dbResponses: []dbResponse{{ 345 query: "select * from t limit 10001", 346 result: selectResult, 347 }, { 348 query: "/* u1@PRIMARY */ select * from t limit 10001", 349 result: selectResult, 350 }}, 351 resultWant: selectResult, 352 planWant: "Select", 353 logWant: "/* u1@PRIMARY */ select * from t limit 10001", 354 inTxWant: "/* u1@PRIMARY */ select * from t limit 10001", 355 }} 356 for _, tcase := range testcases { 357 t.Run(tcase.input, func(t *testing.T) { 358 db := setUpQueryExecutorTest(t) 359 defer db.Close() 360 for _, dbr := range tcase.dbResponses { 361 db.AddQuery(dbr.query, dbr.result) 362 } 363 callerID := &querypb.VTGateCallerID{ 364 Username: "u1", 365 } 366 ctx := callerid.NewContext(context.Background(), nil, callerID) 367 tsv := newTestTabletServer(ctx, noFlags, db) 368 tsv.config.DB.DBName = "ks" 369 tsv.config.AnnotateQueries = true 370 defer tsv.StopService() 371 372 tsv.SetPassthroughDMLs(tcase.passThrough) 373 374 // Test outside a transaction. 375 qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) 376 got, err := qre.Execute() 377 require.NoError(t, err, tcase.input) 378 assert.Equal(t, tcase.resultWant, got, tcase.input) 379 assert.Equal(t, tcase.planWant, qre.logStats.PlanType, tcase.input) 380 assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) 381 382 // Wait for the existing query to be processed by the cache 383 tsv.QueryPlanCacheWait() 384 385 // Test inside a transaction. 386 target := tsv.sm.Target() 387 state, err := tsv.Begin(ctx, target, nil) 388 require.NoError(t, err) 389 require.NotNil(t, state.TabletAlias, "alias should not be nil") 390 assert.Equal(t, tsv.alias, state.TabletAlias, "Wrong alias returned by Begin") 391 defer tsv.Commit(ctx, target, state.TransactionID) 392 393 qre = newTestQueryExecutor(ctx, tsv, tcase.input, state.TransactionID) 394 got, err = qre.Execute() 395 require.NoError(t, err, tcase.input) 396 assert.Equal(t, tcase.resultWant, got, "in tx: %v", tcase.input) 397 assert.Equal(t, tcase.planWant, qre.logStats.PlanType, "in tx: %v", tcase.input) 398 want := tcase.logWant 399 if tcase.inTxWant != "" { 400 want = tcase.inTxWant 401 } 402 assert.Equal(t, want, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) 403 }) 404 } 405 } 406 407 // TestQueryExecutorSelectImpossible is separate because it's a special case 408 // because the "in transaction" case is a no-op. 409 func TestQueryExecutorSelectImpossible(t *testing.T) { 410 type dbResponse struct { 411 query string 412 result *sqltypes.Result 413 } 414 415 fields := sqltypes.MakeTestFields("a|b", "int64|varchar") 416 fieldResult := sqltypes.MakeTestResult(fields) 417 418 testcases := []struct { 419 input string 420 dbResponses []dbResponse 421 resultWant *sqltypes.Result 422 planWant string 423 logWant string 424 inTxWant string 425 }{{ 426 input: "select * from t where 1 != 1", 427 dbResponses: []dbResponse{{ 428 query: "select * from t where 1 != 1 limit 10001", 429 result: fieldResult, 430 }}, 431 resultWant: fieldResult, 432 planWant: "SelectImpossible", 433 logWant: "select * from t where 1 != 1 limit 10001", 434 inTxWant: "select * from t where 1 != 1 limit 10001", 435 }} 436 for _, tcase := range testcases { 437 func() { 438 db := setUpQueryExecutorTest(t) 439 defer db.Close() 440 for _, dbr := range tcase.dbResponses { 441 db.AddQuery(dbr.query, dbr.result) 442 } 443 ctx := context.Background() 444 tsv := newTestTabletServer(ctx, noFlags, db) 445 defer tsv.StopService() 446 447 qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) 448 got, err := qre.Execute() 449 require.NoError(t, err, tcase.input) 450 assert.Equal(t, tcase.resultWant, got, tcase.input) 451 assert.Equal(t, tcase.planWant, qre.logStats.PlanType, tcase.input) 452 assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) 453 target := tsv.sm.Target() 454 state, err := tsv.Begin(ctx, target, nil) 455 require.NoError(t, err) 456 require.NotNil(t, state.TabletAlias, "alias should not be nil") 457 assert.Equal(t, tsv.alias, state.TabletAlias, "Wrong tablet alias from Begin") 458 defer tsv.Commit(ctx, target, state.TransactionID) 459 460 qre = newTestQueryExecutor(ctx, tsv, tcase.input, state.TransactionID) 461 got, err = qre.Execute() 462 require.NoError(t, err, tcase.input) 463 assert.Equal(t, tcase.resultWant, got, "in tx: %v", tcase.input) 464 assert.Equal(t, tcase.planWant, qre.logStats.PlanType, "in tx: %v", tcase.input) 465 assert.Equal(t, tcase.inTxWant, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) 466 }() 467 } 468 } 469 470 // TestDisableOnlineDDL checks whether disabling online DDLs throws the correct error or not 471 func TestDisableOnlineDDL(t *testing.T) { 472 db := setUpQueryExecutorTest(t) 473 defer db.Close() 474 query := "ALTER VITESS_MIGRATION CANCEL ALL" 475 476 db.SetNeverFail(true) 477 defer db.SetNeverFail(false) 478 479 ctx := context.Background() 480 tsv := newTestTabletServer(ctx, noFlags, db) 481 482 qre := newTestQueryExecutor(ctx, tsv, query, 0) 483 _, err := qre.Execute() 484 require.NoError(t, err) 485 tsv.StopService() 486 487 tsv = newTestTabletServer(ctx, disableOnlineDDL, db) 488 defer tsv.StopService() 489 490 qre = newTestQueryExecutor(ctx, tsv, query, 0) 491 _, err = qre.Execute() 492 require.EqualError(t, err, "online ddl is disabled") 493 } 494 495 func TestQueryExecutorLimitFailure(t *testing.T) { 496 type dbResponse struct { 497 query string 498 result *sqltypes.Result 499 } 500 501 dmlResult := &sqltypes.Result{ 502 RowsAffected: 3, 503 } 504 fields := sqltypes.MakeTestFields("a|b", "int64|varchar") 505 fieldResult := sqltypes.MakeTestResult(fields) 506 selectResult := sqltypes.MakeTestResult(fields, "1|aaa", "2|bbb", "3|ccc") 507 508 // The queries are run both in and outside a transaction. 509 testcases := []struct { 510 input string 511 dbResponses []dbResponse 512 err string 513 logWant string 514 inTxWant string 515 testRollback bool 516 }{{ 517 input: "select * from t", 518 dbResponses: []dbResponse{{ 519 query: "select * from t where 1 != 1", 520 result: fieldResult, 521 }, { 522 query: "select * from t limit 3", 523 result: selectResult, 524 }}, 525 err: "count exceeded", 526 logWant: "select * from t limit 3", 527 inTxWant: "select * from t limit 3", 528 }, { 529 input: "update test_table set a=1", 530 dbResponses: []dbResponse{{ 531 query: "update test_table set a = 1 limit 3", 532 result: dmlResult, 533 }}, 534 err: "count exceeded", 535 logWant: "begin; update test_table set a = 1 limit 3; rollback", 536 inTxWant: "update test_table set a = 1 limit 3; rollback", 537 testRollback: true, 538 }, { 539 input: "delete from test_table", 540 dbResponses: []dbResponse{{ 541 query: "delete from test_table limit 3", 542 result: dmlResult, 543 }}, 544 err: "count exceeded", 545 logWant: "begin; delete from test_table limit 3; rollback", 546 inTxWant: "delete from test_table limit 3; rollback", 547 testRollback: true, 548 }, { 549 // There should be no rollback on normal failures. 550 input: "update test_table set a=1", 551 dbResponses: nil, 552 err: "not supported", 553 logWant: "begin; update test_table set a = 1 limit 3; rollback", 554 inTxWant: "update test_table set a = 1 limit 3", 555 }, { 556 // There should be no rollback on normal failures. 557 input: "delete from test_table", 558 dbResponses: nil, 559 err: "not supported", 560 logWant: "begin; delete from test_table limit 3; rollback", 561 inTxWant: "delete from test_table limit 3", 562 }} 563 for i, tcase := range testcases { 564 t.Run(fmt.Sprintf("%d - %s", i, tcase.input), func(t *testing.T) { 565 db := setUpQueryExecutorTest(t) 566 defer db.Close() 567 for _, dbr := range tcase.dbResponses { 568 db.AddQuery(dbr.query, dbr.result) 569 } 570 ctx := callerid.NewContext(context.Background(), callerid.NewEffectiveCallerID("a", "b", "c"), callerid.NewImmediateCallerID("d")) 571 tsv := newTestTabletServer(ctx, smallResultSize, db) 572 defer tsv.StopService() 573 574 tsv.SetPassthroughDMLs(false) 575 576 // Test outside a transaction. 577 qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) 578 _, err := qre.Execute() 579 assert.Error(t, err) 580 assert.Contains(t, err.Error(), tcase.err) 581 assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) 582 583 // Test inside a transaction. 584 target := tsv.sm.Target() 585 state, err := tsv.Begin(ctx, target, nil) 586 require.NoError(t, err) 587 require.NotNil(t, state.TabletAlias, "alias should not be nil") 588 assert.Equal(t, tsv.alias, state.TabletAlias, "Wrong tablet alias from Begin") 589 defer tsv.Commit(ctx, target, state.TransactionID) 590 591 qre = newTestQueryExecutor(ctx, tsv, tcase.input, state.TransactionID) 592 _, err = qre.Execute() 593 assert.Error(t, err) 594 assert.Contains(t, err.Error(), tcase.err) 595 596 want := tcase.logWant 597 if tcase.inTxWant != "" { 598 want = tcase.inTxWant 599 } 600 assert.Equal(t, want, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) 601 602 if !tcase.testRollback { 603 return 604 } 605 // Ensure transaction was rolled back. 606 conn, err := tsv.te.txPool.GetAndLock(state.TransactionID, "") 607 require.NoError(t, err) 608 defer conn.Release(tx.TxClose) 609 610 require.False(t, conn.IsInTransaction(), "connection is still in a transaction") 611 }) 612 } 613 } 614 615 func TestQueryExecutorPlanPassSelectWithLockOutsideATransaction(t *testing.T) { 616 db := setUpQueryExecutorTest(t) 617 defer db.Close() 618 query := "select * from test_table for update" 619 want := &sqltypes.Result{ 620 Fields: getTestTableFields(), 621 Rows: [][]sqltypes.Value{}, 622 } 623 db.AddQuery(query, want) 624 db.AddQuery("select * from test_table limit 10001 for update", &sqltypes.Result{ 625 Fields: getTestTableFields(), 626 }) 627 ctx := context.Background() 628 tsv := newTestTabletServer(ctx, noFlags, db) 629 qre := newTestQueryExecutor(ctx, tsv, query, 0) 630 defer tsv.StopService() 631 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 632 _, err := qre.Execute() 633 assert.NoError(t, err) 634 } 635 636 func TestQueryExecutorPlanNextval(t *testing.T) { 637 db := setUpQueryExecutorTest(t) 638 defer db.Close() 639 selQuery := "select next_id, cache from seq where id = 0 for update" 640 db.AddQuery(selQuery, &sqltypes.Result{ 641 Fields: []*querypb.Field{ 642 {Type: sqltypes.Int64}, 643 {Type: sqltypes.Int64}, 644 }, 645 Rows: [][]sqltypes.Value{{ 646 sqltypes.NewInt64(1), 647 sqltypes.NewInt64(3), 648 }}, 649 }) 650 updateQuery := "update seq set next_id = 4 where id = 0" 651 db.AddQuery(updateQuery, &sqltypes.Result{}) 652 ctx := context.Background() 653 tsv := newTestTabletServer(ctx, noFlags, db) 654 defer tsv.StopService() 655 qre := newTestQueryExecutor(ctx, tsv, "select next value from seq", 0) 656 assert.Equal(t, planbuilder.PlanNextval, qre.plan.PlanID) 657 got, err := qre.Execute() 658 if err != nil { 659 t.Fatalf("qre.Execute() = %v, want nil", err) 660 } 661 want := &sqltypes.Result{ 662 Fields: []*querypb.Field{{ 663 Name: "nextval", 664 Type: sqltypes.Int64, 665 }}, 666 Rows: [][]sqltypes.Value{{ 667 sqltypes.NewInt64(1), 668 }}, 669 } 670 assert.Equal(t, want, got) 671 672 // At this point, NextVal==2, LastVal==4. 673 // So, a single value gen should not cause a db access. 674 db.DeleteQuery(selQuery) 675 qre = newTestQueryExecutor(ctx, tsv, "select next 1 values from seq", 0) 676 got, err = qre.Execute() 677 if err != nil { 678 t.Fatalf("qre.Execute() = %v, want nil", err) 679 } 680 want = &sqltypes.Result{ 681 Fields: []*querypb.Field{{ 682 Name: "nextval", 683 Type: sqltypes.Int64, 684 }}, 685 Rows: [][]sqltypes.Value{{ 686 sqltypes.NewInt64(2), 687 }}, 688 } 689 if !got.Equal(want) { 690 t.Fatalf("qre.Execute() =\n%#v, want:\n%#v", got, want) 691 } 692 693 // NextVal==3, LastVal==4 694 // Let's try the next 2 values. 695 db.AddQuery(selQuery, &sqltypes.Result{ 696 Fields: []*querypb.Field{ 697 {Type: sqltypes.Int64}, 698 {Type: sqltypes.Int64}, 699 }, 700 Rows: [][]sqltypes.Value{{ 701 sqltypes.NewInt64(4), 702 sqltypes.NewInt64(3), 703 }}, 704 }) 705 updateQuery = "update seq set next_id = 7 where id = 0" 706 db.AddQuery(updateQuery, &sqltypes.Result{}) 707 qre = newTestQueryExecutor(ctx, tsv, "select next 2 values from seq", 0) 708 got, err = qre.Execute() 709 if err != nil { 710 t.Fatalf("qre.Execute() = %v, want nil", err) 711 } 712 want = &sqltypes.Result{ 713 Fields: []*querypb.Field{{ 714 Name: "nextval", 715 Type: sqltypes.Int64, 716 }}, 717 Rows: [][]sqltypes.Value{{ 718 sqltypes.NewInt64(3), 719 }}, 720 } 721 if !got.Equal(want) { 722 t.Fatalf("qre.Execute() =\n%#v, want:\n%#v", got, want) 723 } 724 725 // NextVal==5, LastVal==7 726 // Let's try jumping a full cache range. 727 db.AddQuery(selQuery, &sqltypes.Result{ 728 Fields: []*querypb.Field{ 729 {Type: sqltypes.Int64}, 730 {Type: sqltypes.Int64}, 731 }, 732 Rows: [][]sqltypes.Value{{ 733 sqltypes.NewInt64(7), 734 sqltypes.NewInt64(3), 735 }}, 736 }) 737 updateQuery = "update seq set next_id = 13 where id = 0" 738 db.AddQuery(updateQuery, &sqltypes.Result{}) 739 qre = newTestQueryExecutor(ctx, tsv, "select next 6 values from seq", 0) 740 got, err = qre.Execute() 741 if err != nil { 742 t.Fatalf("qre.Execute() = %v, want nil", err) 743 } 744 want = &sqltypes.Result{ 745 Fields: []*querypb.Field{{ 746 Name: "nextval", 747 Type: sqltypes.Int64, 748 }}, 749 Rows: [][]sqltypes.Value{{ 750 sqltypes.NewInt64(5), 751 }}, 752 } 753 if !got.Equal(want) { 754 t.Fatalf("qre.Execute() =\n%#v, want:\n%#v", got, want) 755 } 756 } 757 758 func TestQueryExecutorMessageStreamACL(t *testing.T) { 759 aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) 760 tableacl.Register(aclName, &simpleacl.Factory{}) 761 tableacl.SetDefaultACL(aclName) 762 config := &tableaclpb.Config{ 763 TableGroups: []*tableaclpb.TableGroupSpec{{ 764 Name: "group02", 765 TableNamesOrPrefixes: []string{"msg"}, 766 Readers: []string{"u2"}, 767 Writers: []string{"u1"}, 768 }}, 769 } 770 if err := tableacl.InitFromProto(config); err != nil { 771 t.Fatalf("unable to load tableacl config, error: %v", err) 772 } 773 774 db := setUpQueryExecutorTest(t) 775 defer db.Close() 776 777 tsv := newTestTabletServer(ctx, enableStrictTableACL, db) 778 defer tsv.StopService() 779 780 plan, err := tsv.qe.GetMessageStreamPlan("msg") 781 if err != nil { 782 t.Fatal(err) 783 } 784 785 callerID := &querypb.VTGateCallerID{ 786 Username: "u1", 787 } 788 ctx := callerid.NewContext(context.Background(), nil, callerID) 789 qre := &QueryExecutor{ 790 ctx: ctx, 791 query: "stream from msg", 792 plan: plan, 793 logStats: tabletenv.NewLogStats(ctx, "TestQueryExecutor"), 794 tsv: tsv, 795 } 796 797 // Should not fail because u1 has permission. 798 err = qre.MessageStream(func(qr *sqltypes.Result) error { 799 return io.EOF 800 }) 801 if err != nil { 802 t.Fatal(err) 803 } 804 805 callerID = &querypb.VTGateCallerID{ 806 Username: "u2", 807 Groups: []string{"non-admin"}, 808 } 809 qre.ctx = callerid.NewContext(context.Background(), nil, callerID) 810 // Should fail because u2 does not have permission. 811 err = qre.MessageStream(func(qr *sqltypes.Result) error { 812 return io.EOF 813 }) 814 815 assert.EqualError(t, err, `MessageStream command denied to user 'u2', in groups [non-admin], for table 'msg' (ACL check error)`) 816 if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED { 817 t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_PERMISSION_DENIED) 818 } 819 } 820 821 func TestQueryExecutorTableAcl(t *testing.T) { 822 aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) 823 tableacl.Register(aclName, &simpleacl.Factory{}) 824 tableacl.SetDefaultACL(aclName) 825 db := setUpQueryExecutorTest(t) 826 defer db.Close() 827 query := "select * from test_table limit 1000" 828 want := &sqltypes.Result{ 829 Fields: getTestTableFields(), 830 } 831 db.AddQuery(query, want) 832 db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ 833 Fields: getTestTableFields(), 834 }) 835 836 username := "u2" 837 callerID := &querypb.VTGateCallerID{ 838 Username: username, 839 } 840 ctx := callerid.NewContext(context.Background(), nil, callerID) 841 config := &tableaclpb.Config{ 842 TableGroups: []*tableaclpb.TableGroupSpec{{ 843 Name: "group01", 844 TableNamesOrPrefixes: []string{"test_table"}, 845 Readers: []string{username}, 846 }}, 847 } 848 if err := tableacl.InitFromProto(config); err != nil { 849 t.Fatalf("unable to load tableacl config, error: %v", err) 850 } 851 852 tsv := newTestTabletServer(ctx, noFlags, db) 853 qre := newTestQueryExecutor(ctx, tsv, query, 0) 854 defer tsv.StopService() 855 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 856 got, err := qre.Execute() 857 if err != nil { 858 t.Fatalf("got: %v, want nil", err) 859 } 860 if !got.Equal(want) { 861 t.Fatalf("qre.Execute() = %v, want: %v", got, want) 862 } 863 } 864 865 func TestQueryExecutorTableAclNoPermission(t *testing.T) { 866 aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) 867 tableacl.Register(aclName, &simpleacl.Factory{}) 868 tableacl.SetDefaultACL(aclName) 869 db := setUpQueryExecutorTest(t) 870 defer db.Close() 871 query := "select * from test_table limit 1000" 872 want := &sqltypes.Result{ 873 Fields: getTestTableFields(), 874 } 875 db.AddQuery(query, want) 876 db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ 877 Fields: getTestTableFields(), 878 }) 879 880 username := "u2" 881 callerID := &querypb.VTGateCallerID{ 882 Username: username, 883 } 884 ctx := callerid.NewContext(context.Background(), nil, callerID) 885 config := &tableaclpb.Config{ 886 TableGroups: []*tableaclpb.TableGroupSpec{{ 887 Name: "group02", 888 TableNamesOrPrefixes: []string{"test_table"}, 889 Readers: []string{"superuser"}, 890 }}, 891 } 892 893 if err := tableacl.InitFromProto(config); err != nil { 894 t.Fatalf("unable to load tableacl config, error: %v", err) 895 } 896 // without enabling Config.StrictTableAcl 897 tsv := newTestTabletServer(ctx, noFlags, db) 898 qre := newTestQueryExecutor(ctx, tsv, query, 0) 899 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 900 got, err := qre.Execute() 901 if err != nil { 902 t.Fatalf("got: %v, want nil", err) 903 } 904 if !got.Equal(want) { 905 t.Fatalf("qre.Execute() = %v, want: %v", got, want) 906 } 907 tsv.StopService() 908 909 // enable Config.StrictTableAcl 910 tsv = newTestTabletServer(ctx, enableStrictTableACL, db) 911 qre = newTestQueryExecutor(ctx, tsv, query, 0) 912 defer tsv.StopService() 913 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 914 // query should fail because current user do not have read permissions 915 _, err = qre.Execute() 916 if err == nil { 917 t.Fatal("got: nil, want: error") 918 } 919 if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED { 920 t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_PERMISSION_DENIED) 921 } 922 } 923 924 func TestQueryExecutorTableAclDualTableExempt(t *testing.T) { 925 aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) 926 tableacl.Register(aclName, &simpleacl.Factory{}) 927 tableacl.SetDefaultACL(aclName) 928 db := setUpQueryExecutorTest(t) 929 defer db.Close() 930 931 callerID := &querypb.VTGateCallerID{ 932 Username: "basic_username", 933 } 934 ctx := callerid.NewContext(context.Background(), nil, callerID) 935 936 config := &tableaclpb.Config{ 937 TableGroups: []*tableaclpb.TableGroupSpec{}, 938 } 939 940 if err := tableacl.InitFromProto(config); err != nil { 941 t.Fatalf("unable to load tableacl config, error: %v", err) 942 } 943 944 // enable Config.StrictTableAcl 945 tsv := newTestTabletServer(ctx, enableStrictTableACL, db) 946 query := "select * from test_table where 1 != 1" 947 qre := newTestQueryExecutor(ctx, tsv, query, 0) 948 defer tsv.StopService() 949 assert.Equal(t, planbuilder.PlanSelectImpossible, qre.plan.PlanID) 950 // query should fail because nobody has read access to test_table 951 _, err := qre.Execute() 952 if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED { 953 t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_PERMISSION_DENIED) 954 } 955 956 assert.EqualError(t, err, `SelectImpossible command denied to user 'basic_username' for table 'test_table' (ACL check error)`) 957 958 // table acl should be ignored when querying against dual table 959 query = "select @@version_comment from dual limit 1" 960 ctx = callerid.NewContext(context.Background(), nil, callerID) 961 qre = newTestQueryExecutor(ctx, tsv, query, 0) 962 _, err = qre.Execute() 963 if err != nil { 964 t.Fatalf("qre.Execute: %v, want: nil", err) 965 } 966 967 query = "(select 0 as x from dual where 1 != 1) union (select 1 as y from dual where 1 != 1)" 968 ctx = callerid.NewContext(context.Background(), nil, callerID) 969 qre = newTestQueryExecutor(ctx, tsv, query, 0) 970 _, err = qre.Execute() 971 if err != nil { 972 t.Fatalf("qre.Execute: %v, want: nil", err) 973 } 974 } 975 976 func TestQueryExecutorTableAclExemptACL(t *testing.T) { 977 aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) 978 tableacl.Register(aclName, &simpleacl.Factory{}) 979 tableacl.SetDefaultACL(aclName) 980 db := setUpQueryExecutorTest(t) 981 defer db.Close() 982 query := "select * from test_table limit 1000" 983 want := &sqltypes.Result{ 984 Fields: getTestTableFields(), 985 Rows: [][]sqltypes.Value{}, 986 } 987 db.AddQuery(query, want) 988 db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ 989 Fields: getTestTableFields(), 990 }) 991 992 username := "u2" 993 callerID := &querypb.VTGateCallerID{ 994 Username: username, 995 Groups: []string{"eng", "beta"}, 996 } 997 ctx := callerid.NewContext(context.Background(), nil, callerID) 998 999 config := &tableaclpb.Config{ 1000 TableGroups: []*tableaclpb.TableGroupSpec{{ 1001 Name: "group02", 1002 TableNamesOrPrefixes: []string{"test_table"}, 1003 Readers: []string{"u1"}, 1004 }}, 1005 } 1006 1007 if err := tableacl.InitFromProto(config); err != nil { 1008 t.Fatalf("unable to load tableacl config, error: %v", err) 1009 } 1010 1011 // enable Config.StrictTableAcl 1012 tsv := newTestTabletServer(ctx, enableStrictTableACL, db) 1013 qre := newTestQueryExecutor(ctx, tsv, query, 0) 1014 defer tsv.StopService() 1015 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 1016 // query should fail because current user do not have read permissions 1017 _, err := qre.Execute() 1018 if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED { 1019 t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_PERMISSION_DENIED) 1020 } 1021 assert.EqualError(t, err, `Select command denied to user 'u2', in groups [eng, beta], for table 'test_table' (ACL check error)`) 1022 1023 // table acl should be ignored since this is an exempt user. 1024 username = "exempt-acl" 1025 f, _ := tableacl.GetCurrentACLFactory() 1026 if tsv.qe.exemptACL, err = f.New([]string{username}); err != nil { 1027 t.Fatalf("Cannot load exempt ACL for Table ACL: %v", err) 1028 } 1029 callerID = &querypb.VTGateCallerID{ 1030 Username: username, 1031 } 1032 ctx = callerid.NewContext(context.Background(), nil, callerID) 1033 1034 qre = newTestQueryExecutor(ctx, tsv, query, 0) 1035 _, err = qre.Execute() 1036 if err != nil { 1037 t.Fatal("qre.Execute: nil, want: error") 1038 } 1039 } 1040 1041 func TestQueryExecutorTableAclDryRun(t *testing.T) { 1042 aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) 1043 tableacl.Register(aclName, &simpleacl.Factory{}) 1044 tableacl.SetDefaultACL(aclName) 1045 db := setUpQueryExecutorTest(t) 1046 defer db.Close() 1047 query := "select * from test_table limit 1000" 1048 want := &sqltypes.Result{ 1049 Fields: getTestTableFields(), 1050 Rows: [][]sqltypes.Value{}, 1051 } 1052 db.AddQuery(query, want) 1053 db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ 1054 Fields: getTestTableFields(), 1055 }) 1056 1057 username := "u2" 1058 callerID := &querypb.VTGateCallerID{ 1059 Username: username, 1060 } 1061 ctx := callerid.NewContext(context.Background(), nil, callerID) 1062 1063 config := &tableaclpb.Config{ 1064 TableGroups: []*tableaclpb.TableGroupSpec{{ 1065 Name: "group02", 1066 TableNamesOrPrefixes: []string{"test_table"}, 1067 Readers: []string{"u1"}, 1068 }}, 1069 } 1070 1071 if err := tableacl.InitFromProto(config); err != nil { 1072 t.Fatalf("unable to load tableacl config, error: %v", err) 1073 } 1074 1075 tableACLStatsKey := strings.Join([]string{ 1076 "test_table", 1077 "group02", 1078 planbuilder.PlanSelect.String(), 1079 username, 1080 }, ".") 1081 // enable Config.StrictTableAcl 1082 tsv := newTestTabletServer(ctx, enableStrictTableACL, db) 1083 tsv.qe.enableTableACLDryRun = true 1084 qre := newTestQueryExecutor(ctx, tsv, query, 0) 1085 defer tsv.StopService() 1086 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 1087 beforeCount := tsv.stats.TableaclPseudoDenied.Counts()[tableACLStatsKey] 1088 // query should fail because current user do not have read permissions 1089 _, err := qre.Execute() 1090 if err != nil { 1091 t.Fatalf("qre.Execute() = %v, want: nil", err) 1092 } 1093 afterCount := tsv.stats.TableaclPseudoDenied.Counts()[tableACLStatsKey] 1094 if afterCount-beforeCount != 1 { 1095 t.Fatalf("table acl pseudo denied count should increase by one. got: %d, want: %d", afterCount, beforeCount+1) 1096 } 1097 } 1098 1099 func TestQueryExecutorDenyListQRFail(t *testing.T) { 1100 db := setUpQueryExecutorTest(t) 1101 defer db.Close() 1102 query := "select * from test_table where name = 1 limit 1000" 1103 expandedQuery := "select pk from test_table use index (`index`) where name = 1 limit 1000" 1104 expected := &sqltypes.Result{ 1105 Fields: getTestTableFields(), 1106 } 1107 db.AddQuery(query, expected) 1108 db.AddQuery(expandedQuery, expected) 1109 1110 db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ 1111 Fields: getTestTableFields(), 1112 }) 1113 1114 bannedAddr := "127.0.0.1" 1115 bannedUser := "u2" 1116 1117 alterRule := rules.NewQueryRule("disable update", "disable update", rules.QRFail) 1118 alterRule.SetIPCond(bannedAddr) 1119 alterRule.SetUserCond(bannedUser) 1120 alterRule.SetQueryCond("select.*") 1121 alterRule.AddPlanCond(planbuilder.PlanSelect) 1122 alterRule.AddTableCond("test_table") 1123 1124 rulesName := "denyListRulesQRFail" 1125 rules := rules.New() 1126 rules.Add(alterRule) 1127 1128 callInfo := &fakecallinfo.FakeCallInfo{ 1129 Remote: bannedAddr, 1130 User: bannedUser, 1131 } 1132 ctx := callinfo.NewContext(context.Background(), callInfo) 1133 tsv := newTestTabletServer(ctx, noFlags, db) 1134 tsv.qe.queryRuleSources.UnRegisterSource(rulesName) 1135 tsv.qe.queryRuleSources.RegisterSource(rulesName) 1136 defer tsv.qe.queryRuleSources.UnRegisterSource(rulesName) 1137 1138 if err := tsv.qe.queryRuleSources.SetRules(rulesName, rules); err != nil { 1139 t.Fatalf("failed to set rule, error: %v", err) 1140 } 1141 1142 qre := newTestQueryExecutor(ctx, tsv, query, 0) 1143 defer tsv.StopService() 1144 1145 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 1146 // execute should fail because query has a table which is part of the denylist 1147 _, err := qre.Execute() 1148 if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT { 1149 t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) 1150 } 1151 } 1152 1153 func TestQueryExecutorDenyListQRRetry(t *testing.T) { 1154 db := setUpQueryExecutorTest(t) 1155 defer db.Close() 1156 query := "select * from test_table where name = 1 limit 1000" 1157 expandedQuery := "select pk from test_table use index (`index`) where name = 1 limit 1000" 1158 expected := &sqltypes.Result{ 1159 Fields: getTestTableFields(), 1160 } 1161 db.AddQuery(query, expected) 1162 db.AddQuery(expandedQuery, expected) 1163 1164 db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ 1165 Fields: getTestTableFields(), 1166 }) 1167 1168 bannedAddr := "127.0.0.1" 1169 bannedUser := "x" 1170 1171 alterRule := rules.NewQueryRule("disable update", "disable update", rules.QRFailRetry) 1172 alterRule.SetIPCond(bannedAddr) 1173 alterRule.SetUserCond(bannedUser) 1174 alterRule.SetQueryCond("select.*") 1175 alterRule.AddPlanCond(planbuilder.PlanSelect) 1176 alterRule.AddTableCond("test_table") 1177 1178 rulesName := "denyListRulesQRRetry" 1179 rules := rules.New() 1180 rules.Add(alterRule) 1181 1182 callInfo := &fakecallinfo.FakeCallInfo{ 1183 Remote: bannedAddr, 1184 User: bannedUser, 1185 } 1186 ctx := callinfo.NewContext(context.Background(), callInfo) 1187 tsv := newTestTabletServer(ctx, noFlags, db) 1188 tsv.qe.queryRuleSources.UnRegisterSource(rulesName) 1189 tsv.qe.queryRuleSources.RegisterSource(rulesName) 1190 defer tsv.qe.queryRuleSources.UnRegisterSource(rulesName) 1191 1192 if err := tsv.qe.queryRuleSources.SetRules(rulesName, rules); err != nil { 1193 t.Fatalf("failed to set rule, error: %v", err) 1194 } 1195 1196 qre := newTestQueryExecutor(ctx, tsv, query, 0) 1197 defer tsv.StopService() 1198 1199 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 1200 _, err := qre.Execute() 1201 if code := vterrors.Code(err); code != vtrpcpb.Code_FAILED_PRECONDITION { 1202 t.Fatalf("tsv.qe.queryRuleSources.SetRules: %v, want %v", code, vtrpcpb.Code_FAILED_PRECONDITION) 1203 } 1204 } 1205 1206 func TestReplaceSchemaName(t *testing.T) { 1207 db := setUpQueryExecutorTest(t) 1208 defer db.Close() 1209 1210 queryFmt := "select * from information_schema.schema_name where schema_name = %s" 1211 inQuery := fmt.Sprintf(queryFmt, ":"+sqltypes.BvSchemaName) 1212 wantQuery := fmt.Sprintf(queryFmt, fmt.Sprintf( 1213 "'%s' limit %d", 1214 db.Name(), 1215 10001, 1216 )) 1217 wantQueryStream := fmt.Sprintf(queryFmt, fmt.Sprintf( 1218 "'%s'", 1219 db.Name(), 1220 )) 1221 1222 ctx := context.Background() 1223 tsv := newTestTabletServer(ctx, noFlags, db) 1224 defer tsv.StopService() 1225 1226 db.AddQuery(wantQuery, &sqltypes.Result{ 1227 Fields: getTestTableFields(), 1228 }) 1229 1230 db.AddQuery(wantQueryStream, &sqltypes.Result{ 1231 Fields: getTestTableFields(), 1232 }) 1233 1234 // Test non streaming execute. 1235 { 1236 qre := newTestQueryExecutor(ctx, tsv, inQuery, 0) 1237 assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) 1238 // Any value other than nil should cause QueryExecutor to replace the 1239 // schema name. 1240 qre.bindVars[sqltypes.BvReplaceSchemaName] = sqltypes.NullBindVariable 1241 _, err := qre.Execute() 1242 require.NoError(t, err) 1243 _, ok := qre.bindVars[sqltypes.BvSchemaName] 1244 require.True(t, ok) 1245 } 1246 1247 // Test streaming execute. 1248 { 1249 qre := newTestQueryExecutorStreaming(ctx, tsv, inQuery, 0) 1250 // Stream only replaces schema name when plan is PlanSelectStream. 1251 assert.Equal(t, planbuilder.PlanSelectStream, qre.plan.PlanID) 1252 // Any value other than nil should cause QueryExecutor to replace the 1253 // schema name. 1254 qre.bindVars[sqltypes.BvReplaceSchemaName] = sqltypes.NullBindVariable 1255 err := qre.Stream(func(_ *sqltypes.Result) error { 1256 _, ok := qre.bindVars[sqltypes.BvSchemaName] 1257 require.True(t, ok) 1258 return nil 1259 }) 1260 require.NoError(t, err) 1261 } 1262 } 1263 1264 func TestQueryExecutorShouldConsolidate(t *testing.T) { 1265 testcases := []struct { 1266 consolidates []bool 1267 executorFlags executorFlags 1268 name string 1269 // Whether or not query consolidator is requested. 1270 options []querypb.ExecuteOptions_Consolidator 1271 // Whether or not query is consolidated. 1272 queries []string 1273 }{{ 1274 consolidates: []bool{ 1275 false, 1276 false, 1277 false, 1278 true, 1279 }, 1280 executorFlags: noFlags, 1281 name: "vttablet-consolidator-disabled", 1282 options: []querypb.ExecuteOptions_Consolidator{ 1283 querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED, 1284 querypb.ExecuteOptions_CONSOLIDATOR_ENABLED, 1285 querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED, 1286 querypb.ExecuteOptions_CONSOLIDATOR_ENABLED, 1287 }, 1288 queries: []string{ 1289 "select * from t limit 10001", 1290 // The previous query isn't passed to the query consolidator, 1291 // so the next query can't consolidate into it. 1292 "select * from t limit 10001", 1293 "select * from t limit 10001", 1294 // This query should consolidate into the previous query 1295 // that was passed to the consolidator. 1296 "select * from t limit 10001", 1297 }, 1298 }, { 1299 consolidates: []bool{ 1300 false, 1301 true, 1302 false, 1303 true, 1304 false, 1305 }, 1306 executorFlags: enableConsolidator, 1307 name: "consolidator=enabled", 1308 options: []querypb.ExecuteOptions_Consolidator{ 1309 querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED, 1310 querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED, 1311 querypb.ExecuteOptions_CONSOLIDATOR_DISABLED, 1312 querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED, 1313 querypb.ExecuteOptions_CONSOLIDATOR_DISABLED, 1314 }, 1315 queries: []string{ 1316 "select * from t limit 10001", 1317 "select * from t limit 10001", 1318 // This query shouldn't be passed to the consolidator. 1319 "select * from t limit 10001", 1320 "select * from t limit 10001", 1321 // This query shouldn't be passed to the consolidator. 1322 "select * from t limit 10001", 1323 }, 1324 }} 1325 for _, tcase := range testcases { 1326 t.Run(tcase.name, func(t *testing.T) { 1327 db := setUpQueryExecutorTest(t) 1328 1329 ctx := context.Background() 1330 tsv := newTestTabletServer(ctx, tcase.executorFlags, db) 1331 1332 defer db.Close() 1333 defer tsv.StopService() 1334 1335 doneCh := make(chan bool, len(tcase.queries)) 1336 readyCh := make(chan bool, len(tcase.queries)) 1337 var qres []*QueryExecutor 1338 var waitChs []chan bool 1339 1340 for i, input := range tcase.queries { 1341 qre := newTestQueryExecutor(ctx, tsv, input, 0) 1342 qre.options = &querypb.ExecuteOptions{ 1343 Consolidator: tcase.options[i], 1344 } 1345 qres = append(qres, qre) 1346 1347 // If this query is consolidated, don't add a fakesqldb expectation. 1348 if tcase.consolidates[i] { 1349 continue 1350 } 1351 1352 // Set up a query expectation. 1353 waitCh := make(chan bool) 1354 waitChs = append(waitChs, waitCh) 1355 db.AddExpectedExecuteFetchAtIndex(i, fakesqldb.ExpectedExecuteFetch{ 1356 AfterFunc: func() { 1357 // Signal that we're ready to proceed. 1358 readyCh <- true 1359 // Wait until we're signaled to proceed. 1360 <-waitCh 1361 }, 1362 Query: input, 1363 QueryResult: &sqltypes.Result{ 1364 Fields: getTestTableFields(), 1365 }, 1366 }) 1367 } 1368 1369 db.OrderMatters() 1370 db.SetNeverFail(true) 1371 1372 for i, input := range tcase.queries { 1373 qre := qres[i] 1374 go func(i int, input string, qre *QueryExecutor) { 1375 // Execute the query. 1376 _, err := qre.Execute() 1377 1378 require.NoError(t, err, fmt.Sprintf( 1379 "input[%d]=%q,querySources=%v", i, input, qre.logStats.QuerySources, 1380 )) 1381 1382 // Signal that the query is done. 1383 doneCh <- true 1384 }(i, input, qre) 1385 1386 // If this query is consolidated, don't wait for fakesqldb to 1387 // tell us query is ready is ready. 1388 if tcase.consolidates[i] { 1389 continue 1390 } 1391 1392 // Wait until query is queued up before starting next one. 1393 <-readyCh 1394 } 1395 1396 // Signal ready queries to return. 1397 for i := 0; i < len(waitChs); i++ { 1398 close(waitChs[i]) 1399 } 1400 1401 // Wait for queries to finish. 1402 for i := 0; i < len(qres); i++ { 1403 <-doneCh 1404 } 1405 1406 for i := 0; i < len(tcase.consolidates); i++ { 1407 input := tcase.queries[i] 1408 qre := qres[i] 1409 want := tcase.consolidates[i] 1410 got := qre.logStats.QuerySources&tabletenv.QuerySourceConsolidator != 0 1411 1412 require.Equal(t, want, got, fmt.Sprintf( 1413 "input[%d]=%q,querySources=%v", i, input, qre.logStats.QuerySources, 1414 )) 1415 } 1416 1417 db.VerifyAllExecutedOrFail() 1418 }) 1419 } 1420 } 1421 1422 type executorFlags int64 1423 1424 const ( 1425 noFlags executorFlags = 0 1426 enableStrictTableACL = 1 << iota 1427 smallTxPool 1428 noTwopc 1429 shortTwopcAge 1430 smallResultSize 1431 disableOnlineDDL 1432 enableConsolidator 1433 ) 1434 1435 // newTestQueryExecutor uses a package level variable testTabletServer defined in tabletserver_test.go 1436 func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb.DB) *TabletServer { 1437 config := tabletenv.NewDefaultConfig() 1438 config.OltpReadPool.Size = 100 1439 if flags&smallTxPool > 0 { 1440 config.TxPool.Size = 3 1441 } else { 1442 config.TxPool.Size = 100 1443 } 1444 if flags&enableStrictTableACL > 0 { 1445 config.StrictTableACL = true 1446 } else { 1447 config.StrictTableACL = false 1448 } 1449 if flags&noTwopc > 0 { 1450 config.TwoPCEnable = false 1451 } else { 1452 config.TwoPCEnable = true 1453 } 1454 if flags&disableOnlineDDL > 0 { 1455 config.EnableOnlineDDL = false 1456 } else { 1457 config.EnableOnlineDDL = true 1458 } 1459 config.TwoPCCoordinatorAddress = "fake" 1460 if flags&shortTwopcAge > 0 { 1461 config.TwoPCAbandonAge = 0.5 1462 } else { 1463 config.TwoPCAbandonAge = 10 1464 } 1465 if flags&smallResultSize > 0 { 1466 config.Oltp.MaxRows = 2 1467 } 1468 if flags&enableConsolidator > 0 { 1469 config.Consolidator = tabletenv.Enable 1470 } else { 1471 config.Consolidator = tabletenv.Disable 1472 } 1473 dbconfigs := newDBConfigs(db) 1474 config.DB = dbconfigs 1475 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1476 target := &querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1477 err := tsv.StartService(target, dbconfigs, nil /* mysqld */) 1478 if config.TwoPCEnable { 1479 tsv.TwoPCEngineWait() 1480 } 1481 if err != nil { 1482 panic(err) 1483 } 1484 return tsv 1485 } 1486 1487 func newTransaction(tsv *TabletServer, options *querypb.ExecuteOptions) int64 { 1488 target := tsv.sm.Target() 1489 state, err := tsv.Begin(context.Background(), target, options) 1490 if err != nil { 1491 panic(vterrors.Wrap(err, "failed to start a transaction")) 1492 } 1493 return state.TransactionID 1494 } 1495 1496 func newTestQueryExecutor(ctx context.Context, tsv *TabletServer, sql string, txID int64) *QueryExecutor { 1497 logStats := tabletenv.NewLogStats(ctx, "TestQueryExecutor") 1498 plan, err := tsv.qe.GetPlan(ctx, logStats, sql, false) 1499 if err != nil { 1500 panic(err) 1501 } 1502 return &QueryExecutor{ 1503 ctx: ctx, 1504 query: sql, 1505 bindVars: make(map[string]*querypb.BindVariable), 1506 connID: txID, 1507 plan: plan, 1508 logStats: logStats, 1509 tsv: tsv, 1510 } 1511 } 1512 1513 func newTestQueryExecutorStreaming(ctx context.Context, tsv *TabletServer, sql string, txID int64) *QueryExecutor { 1514 logStats := tabletenv.NewLogStats(ctx, "TestQueryExecutorStreaming") 1515 plan, err := tsv.qe.GetStreamPlan(sql) 1516 if err != nil { 1517 panic(err) 1518 } 1519 return &QueryExecutor{ 1520 ctx: ctx, 1521 query: sql, 1522 bindVars: make(map[string]*querypb.BindVariable), 1523 connID: txID, 1524 plan: plan, 1525 logStats: logStats, 1526 tsv: tsv, 1527 } 1528 } 1529 1530 func setUpQueryExecutorTest(t *testing.T) *fakesqldb.DB { 1531 db := fakesqldb.New(t) 1532 initQueryExecutorTestDB(db) 1533 return db 1534 } 1535 1536 const baseShowTablesPattern = `SELECT t\.table_name.*` 1537 1538 func initQueryExecutorTestDB(db *fakesqldb.DB) { 1539 addQueryExecutorSupportedQueries(db) 1540 db.AddQueryPattern(baseShowTablesPattern, &sqltypes.Result{ 1541 Fields: mysql.BaseShowTablesFields, 1542 Rows: [][]sqltypes.Value{ 1543 mysql.BaseShowTablesRow("test_table", false, ""), 1544 mysql.BaseShowTablesRow("seq", false, "vitess_sequence"), 1545 mysql.BaseShowTablesRow("msg", false, "vitess_message,vt_ack_wait=30,vt_purge_after=120,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=30"), 1546 }, 1547 }) 1548 db.AddQuery("show status like 'Innodb_rows_read'", sqltypes.MakeTestResult(sqltypes.MakeTestFields( 1549 "Variable_name|Value", 1550 "varchar|int64"), 1551 "Innodb_rows_read|0", 1552 )) 1553 sidecardb.AddSchemaInitQueries(db, true) 1554 } 1555 1556 func getTestTableFields() []*querypb.Field { 1557 return []*querypb.Field{ 1558 {Name: "pk", Type: sqltypes.Int32}, 1559 {Name: "name", Type: sqltypes.Int32}, 1560 {Name: "addr", Type: sqltypes.Int32}, 1561 } 1562 } 1563 1564 func addQueryExecutorSupportedQueries(db *fakesqldb.DB) { 1565 queryResultMap := map[string]*sqltypes.Result{ 1566 // queries for schema info 1567 "select unix_timestamp()": { 1568 Fields: []*querypb.Field{{ 1569 Type: sqltypes.Uint64, 1570 }}, 1571 Rows: [][]sqltypes.Value{ 1572 {sqltypes.NewInt32(1427325875)}, 1573 }, 1574 }, 1575 "select @@global.sql_mode": { 1576 Fields: []*querypb.Field{{ 1577 Type: sqltypes.VarChar, 1578 }}, 1579 Rows: [][]sqltypes.Value{ 1580 {sqltypes.NewVarBinary("STRICT_TRANS_TABLES")}, 1581 }, 1582 }, 1583 "select @@autocommit": { 1584 Fields: []*querypb.Field{{ 1585 Type: sqltypes.Uint64, 1586 }}, 1587 Rows: [][]sqltypes.Value{ 1588 {sqltypes.NewVarBinary("1")}, 1589 }, 1590 }, 1591 "select @@sql_auto_is_null": { 1592 Fields: []*querypb.Field{{ 1593 Type: sqltypes.Uint64, 1594 }}, 1595 Rows: [][]sqltypes.Value{ 1596 {sqltypes.NewVarBinary("0")}, 1597 }, 1598 }, 1599 "select @@version_comment from dual where 1 != 1": { 1600 Fields: []*querypb.Field{{ 1601 Type: sqltypes.VarChar, 1602 }}, 1603 }, 1604 "select @@version_comment from dual limit 1": { 1605 Fields: []*querypb.Field{{ 1606 Type: sqltypes.VarChar, 1607 }}, 1608 Rows: [][]sqltypes.Value{ 1609 {sqltypes.NewVarBinary("fakedb server")}, 1610 }, 1611 }, 1612 "select 0 as x from dual where 1 != 1 union select 1 as y from dual where 1 != 1": { 1613 Fields: []*querypb.Field{{ 1614 Type: sqltypes.Uint64, 1615 }}, 1616 Rows: [][]sqltypes.Value{}, 1617 }, 1618 "select 0 as x from dual where 1 != 1 union select 1 as y from dual where 1 != 1 limit 10001": { 1619 Fields: []*querypb.Field{{ 1620 Type: sqltypes.Uint64, 1621 }}, 1622 Rows: [][]sqltypes.Value{}, 1623 }, 1624 "select * from t where 1 != 1 limit 10001": { 1625 Fields: []*querypb.Field{{ 1626 Type: sqltypes.Uint64, 1627 }, { 1628 Type: sqltypes.VarChar, 1629 }}, 1630 Rows: [][]sqltypes.Value{}, 1631 }, 1632 mysql.BaseShowPrimary: { 1633 Fields: mysql.ShowPrimaryFields, 1634 Rows: [][]sqltypes.Value{ 1635 mysql.ShowPrimaryRow("test_table", "pk"), 1636 mysql.ShowPrimaryRow("seq", "id"), 1637 mysql.ShowPrimaryRow("msg", "id"), 1638 }, 1639 }, 1640 "begin": {}, 1641 "commit": {}, 1642 "rollback": {}, 1643 fmt.Sprintf(sqlReadAllRedo, "_vt", "_vt"): {}, 1644 } 1645 1646 sidecardb.AddSchemaInitQueries(db, true) 1647 for query, result := range queryResultMap { 1648 db.AddQuery(query, result) 1649 } 1650 db.MockQueriesForTable("test_table", &sqltypes.Result{ 1651 Fields: []*querypb.Field{{ 1652 Name: "pk", 1653 Type: sqltypes.Int32, 1654 }, { 1655 Name: "name", 1656 Type: sqltypes.Int32, 1657 }, { 1658 Name: "addr", 1659 Type: sqltypes.Int32, 1660 }}, 1661 }) 1662 db.MockQueriesForTable("seq", &sqltypes.Result{ 1663 Fields: []*querypb.Field{{ 1664 Name: "id", 1665 Type: sqltypes.Int32, 1666 }, { 1667 Name: "next_id", 1668 Type: sqltypes.Int64, 1669 }, { 1670 Name: "cache", 1671 Type: sqltypes.Int64, 1672 }, { 1673 Name: "increment", 1674 Type: sqltypes.Int64, 1675 }}, 1676 }) 1677 db.MockQueriesForTable("msg", &sqltypes.Result{ 1678 Fields: []*querypb.Field{{ 1679 Name: "id", 1680 Type: sqltypes.Int64, 1681 }, { 1682 Name: "priority", 1683 Type: sqltypes.Int64, 1684 }, { 1685 Name: "time_next", 1686 Type: sqltypes.Int64, 1687 }, { 1688 Name: "epoch", 1689 Type: sqltypes.Int64, 1690 }, { 1691 Name: "time_acked", 1692 Type: sqltypes.Int64, 1693 }, { 1694 Name: "message", 1695 Type: sqltypes.Int64, 1696 }}, 1697 }) 1698 } 1699 1700 func TestQueryExecSchemaReloadCount(t *testing.T) { 1701 type dbResponse struct { 1702 query string 1703 result *sqltypes.Result 1704 } 1705 1706 dmlResult := &sqltypes.Result{ 1707 RowsAffected: 1, 1708 } 1709 fields := sqltypes.MakeTestFields("a|b", "int64|varchar") 1710 selectResult := sqltypes.MakeTestResult(fields, "1|aaa") 1711 emptyResult := &sqltypes.Result{} 1712 1713 // The queries are run both in and outside a transaction. 1714 testcases := []struct { 1715 // input is the input query. 1716 input string 1717 // dbResponses specifes the list of queries and responses to add to the fake db. 1718 dbResponses []dbResponse 1719 schemaReloadCount int 1720 }{{ 1721 input: "select * from t", 1722 dbResponses: []dbResponse{{ 1723 query: `select \* from t.*`, 1724 result: selectResult, 1725 }}, 1726 }, { 1727 input: "insert into t values(1, 'aaa')", 1728 dbResponses: []dbResponse{{ 1729 query: "insert.*", 1730 result: dmlResult, 1731 }}, 1732 }, { 1733 input: "create table t(a int, b varchar(64))", 1734 dbResponses: []dbResponse{{ 1735 query: "create.*", 1736 result: emptyResult, 1737 }}, 1738 schemaReloadCount: 1, 1739 }, { 1740 input: "drop table t", 1741 dbResponses: []dbResponse{{ 1742 query: "drop.*", 1743 result: dmlResult, 1744 }}, 1745 schemaReloadCount: 1, 1746 }, { 1747 input: "create table t(a int, b varchar(64))", 1748 dbResponses: []dbResponse{{ 1749 query: "create.*", 1750 result: emptyResult, 1751 }}, 1752 schemaReloadCount: 1, 1753 }, { 1754 input: "drop table t", 1755 dbResponses: []dbResponse{{ 1756 query: "drop.*", 1757 result: dmlResult, 1758 }}, 1759 schemaReloadCount: 1, 1760 }} 1761 db := setUpQueryExecutorTest(t) 1762 defer db.Close() 1763 tsv := newTestTabletServer(context.Background(), noFlags, db) 1764 tsv.config.DB.DBName = "ks" 1765 defer tsv.StopService() 1766 for _, tcase := range testcases { 1767 t.Run(tcase.input, func(t *testing.T) { 1768 // reset the schema reload metric before running the test. 1769 tsv.se.SchemaReloadTimings.Reset() 1770 for _, dbr := range tcase.dbResponses { 1771 db.AddQueryPattern(dbr.query, dbr.result) 1772 } 1773 1774 qre := newTestQueryExecutor(context.Background(), tsv, tcase.input, 0) 1775 _, err := qre.Execute() 1776 require.NoError(t, err) 1777 assert.EqualValues(t, tcase.schemaReloadCount, qre.tsv.se.SchemaReloadTimings.Counts()["TabletServerTest.SchemaReload"], "got: %v", qre.tsv.se.SchemaReloadTimings.Counts()) 1778 }) 1779 } 1780 }