vitess.io/vitess@v0.16.2/go/vt/vtgate/executor_select_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 "context" 21 "fmt" 22 "os" 23 "runtime" 24 "strconv" 25 "strings" 26 "testing" 27 "time" 28 29 _flag "vitess.io/vitess/go/internal/flag" 30 31 "vitess.io/vitess/go/vt/sqlparser" 32 33 "github.com/google/go-cmp/cmp" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 "google.golang.org/protobuf/proto" 37 38 "vitess.io/vitess/go/cache" 39 "vitess.io/vitess/go/sqltypes" 40 "vitess.io/vitess/go/test/utils" 41 "vitess.io/vitess/go/vt/discovery" 42 "vitess.io/vitess/go/vt/vterrors" 43 _ "vitess.io/vitess/go/vt/vtgate/vindexes" 44 "vitess.io/vitess/go/vt/vttablet/sandboxconn" 45 46 querypb "vitess.io/vitess/go/vt/proto/query" 47 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 48 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 49 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 50 ) 51 52 func TestSelectNext(t *testing.T) { 53 executor, _, _, sbclookup := createExecutorEnv() 54 55 query := "select next :n values from user_seq" 56 bv := map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(2)} 57 wantQueries := []*querypb.BoundQuery{{ 58 Sql: query, 59 BindVariables: map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(2)}, 60 }} 61 62 // Autocommit 63 session := NewAutocommitSession(&vtgatepb.Session{}) 64 _, err := executor.Execute(context.Background(), "TestSelectNext", session, query, bv) 65 require.NoError(t, err) 66 67 utils.MustMatch(t, wantQueries, sbclookup.Queries) 68 assert.Zero(t, sbclookup.BeginCount.Get()) 69 assert.Zero(t, sbclookup.ReserveCount.Get()) 70 sbclookup.Queries = nil 71 72 // Txn 73 session = NewAutocommitSession(&vtgatepb.Session{}) 74 session.Session.InTransaction = true 75 _, err = executor.Execute(context.Background(), "TestSelectNext", session, query, bv) 76 require.NoError(t, err) 77 78 utils.MustMatch(t, wantQueries, sbclookup.Queries) 79 assert.Zero(t, sbclookup.BeginCount.Get()) 80 assert.Zero(t, sbclookup.ReserveCount.Get()) 81 sbclookup.Queries = nil 82 83 // Reserve 84 session = NewAutocommitSession(&vtgatepb.Session{}) 85 session.Session.InReservedConn = true 86 _, err = executor.Execute(context.Background(), "TestSelectNext", session, query, bv) 87 require.NoError(t, err) 88 89 utils.MustMatch(t, wantQueries, sbclookup.Queries) 90 assert.Zero(t, sbclookup.BeginCount.Get()) 91 assert.Zero(t, sbclookup.ReserveCount.Get()) 92 sbclookup.Queries = nil 93 94 // Reserve and Txn 95 session = NewAutocommitSession(&vtgatepb.Session{}) 96 session.Session.InReservedConn = true 97 session.Session.InTransaction = true 98 _, err = executor.Execute(context.Background(), "TestSelectNext", session, query, bv) 99 require.NoError(t, err) 100 101 utils.MustMatch(t, wantQueries, sbclookup.Queries) 102 assert.Zero(t, sbclookup.BeginCount.Get()) 103 assert.Zero(t, sbclookup.ReserveCount.Get()) 104 } 105 106 func TestSelectDBA(t *testing.T) { 107 executor, sbc1, _, _ := createExecutorEnv() 108 109 query := "select * from INFORMATION_SCHEMA.foo" 110 _, err := executor.Execute(context.Background(), "TestSelectDBA", 111 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 112 query, map[string]*querypb.BindVariable{}, 113 ) 114 require.NoError(t, err) 115 wantQueries := []*querypb.BoundQuery{{Sql: query, BindVariables: map[string]*querypb.BindVariable{}}} 116 utils.MustMatch(t, wantQueries, sbc1.Queries) 117 118 sbc1.Queries = nil 119 query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES ist WHERE ist.table_schema = 'performance_schema' AND ist.table_name = 'foo'" 120 _, err = executor.Execute(context.Background(), "TestSelectDBA", 121 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 122 query, map[string]*querypb.BindVariable{}, 123 ) 124 require.NoError(t, err) 125 wantQueries = []*querypb.BoundQuery{{Sql: "select count(*) from INFORMATION_SCHEMA.`TABLES` as ist where ist.table_schema = :__vtschemaname and ist.table_name = :ist_table_name", 126 BindVariables: map[string]*querypb.BindVariable{ 127 "__vtschemaname": sqltypes.StringBindVariable("performance_schema"), 128 "ist_table_name": sqltypes.StringBindVariable("foo"), 129 }}} 130 utils.MustMatch(t, wantQueries, sbc1.Queries) 131 132 sbc1.Queries = nil 133 query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks' and table_name = 'user'" 134 _, err = executor.Execute(context.Background(), "TestSelectDBA", 135 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 136 query, map[string]*querypb.BindVariable{}, 137 ) 138 require.NoError(t, err) 139 wantQueries = []*querypb.BoundQuery{{Sql: "select 1 from information_schema.table_constraints where constraint_schema = :__vtschemaname and table_name = :table_name", 140 BindVariables: map[string]*querypb.BindVariable{ 141 "__vtschemaname": sqltypes.StringBindVariable("vt_ks"), 142 "table_name": sqltypes.StringBindVariable("user"), 143 }}} 144 utils.MustMatch(t, wantQueries, sbc1.Queries) 145 146 sbc1.Queries = nil 147 query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks'" 148 _, err = executor.Execute(context.Background(), "TestSelectDBA", 149 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 150 query, map[string]*querypb.BindVariable{}, 151 ) 152 require.NoError(t, err) 153 wantQueries = []*querypb.BoundQuery{{Sql: "select 1 from information_schema.table_constraints where constraint_schema = :__vtschemaname", 154 BindVariables: map[string]*querypb.BindVariable{ 155 "__vtschemaname": sqltypes.StringBindVariable("vt_ks"), 156 }}} 157 utils.MustMatch(t, wantQueries, sbc1.Queries) 158 } 159 160 func TestSystemVariablesMySQLBelow80(t *testing.T) { 161 executor, sbc1, _, _ := createExecutorEnv() 162 executor.normalize = true 163 164 sqlparser.SetParserVersion("57000") 165 setVarEnabled = true 166 167 session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) 168 169 sbc1.SetResults([]*sqltypes.Result{{ 170 Fields: []*querypb.Field{ 171 {Name: "orig", Type: sqltypes.VarChar}, 172 {Name: "new", Type: sqltypes.VarChar}, 173 }, 174 Rows: [][]sqltypes.Value{{ 175 sqltypes.NewVarChar(""), 176 sqltypes.NewVarChar("only_full_group_by"), 177 }}, 178 }}) 179 180 _, err := executor.Execute(context.Background(), "TestSetStmt", session, "set @@sql_mode = only_full_group_by", map[string]*querypb.BindVariable{}) 181 require.NoError(t, err) 182 183 _, err = executor.Execute(context.Background(), "TestSelect", session, "select 1 from information_schema.table", map[string]*querypb.BindVariable{}) 184 require.NoError(t, err) 185 require.True(t, session.InReservedConn()) 186 187 wantQueries := []*querypb.BoundQuery{ 188 {Sql: "select @@sql_mode orig, 'only_full_group_by' new"}, 189 {Sql: "set sql_mode = 'only_full_group_by'", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 190 {Sql: "select :vtg1 from information_schema.`table`", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 191 } 192 193 utils.MustMatch(t, wantQueries, sbc1.Queries) 194 } 195 196 func TestSystemVariablesWithSetVarDisabled(t *testing.T) { 197 executor, sbc1, _, _ := createExecutorEnv() 198 executor.normalize = true 199 200 sqlparser.SetParserVersion("80000") 201 setVarEnabled = false 202 defer func() { 203 setVarEnabled = true 204 }() 205 session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) 206 207 sbc1.SetResults([]*sqltypes.Result{{ 208 Fields: []*querypb.Field{ 209 {Name: "orig", Type: sqltypes.VarChar}, 210 {Name: "new", Type: sqltypes.VarChar}, 211 }, 212 Rows: [][]sqltypes.Value{{ 213 sqltypes.NewVarChar(""), 214 sqltypes.NewVarChar("only_full_group_by"), 215 }}, 216 }}) 217 218 _, err := executor.Execute(context.Background(), "TestSetStmt", session, "set @@sql_mode = only_full_group_by", map[string]*querypb.BindVariable{}) 219 require.NoError(t, err) 220 221 _, err = executor.Execute(context.Background(), "TestSelect", session, "select 1 from information_schema.table", map[string]*querypb.BindVariable{}) 222 require.NoError(t, err) 223 require.True(t, session.InReservedConn()) 224 225 wantQueries := []*querypb.BoundQuery{ 226 {Sql: "select @@sql_mode orig, 'only_full_group_by' new"}, 227 {Sql: "set sql_mode = 'only_full_group_by'", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 228 {Sql: "select :vtg1 from information_schema.`table`", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 229 } 230 231 utils.MustMatch(t, wantQueries, sbc1.Queries) 232 } 233 234 func TestSetSystemVariablesTx(t *testing.T) { 235 executor, sbc1, _, _ := createExecutorEnv() 236 executor.normalize = true 237 238 sqlparser.SetParserVersion("80001") 239 240 session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) 241 242 _, err := executor.Execute(context.Background(), "TestBegin", session, "begin", map[string]*querypb.BindVariable{}) 243 require.NoError(t, err) 244 245 _, err = executor.Execute(context.Background(), "TestSelect", session, "select 1 from information_schema.table", map[string]*querypb.BindVariable{}) 246 require.NoError(t, err) 247 require.NotZero(t, session.ShardSessions) 248 249 sbc1.SetResults([]*sqltypes.Result{{ 250 Fields: []*querypb.Field{ 251 {Name: "orig", Type: sqltypes.VarChar}, 252 {Name: "new", Type: sqltypes.VarChar}, 253 }, 254 Rows: [][]sqltypes.Value{{ 255 sqltypes.NewVarChar(""), 256 sqltypes.NewVarChar("only_full_group_by"), 257 }}, 258 }}) 259 260 _, err = executor.Execute(context.Background(), "TestSetStmt", session, "set @@sql_mode = only_full_group_by", map[string]*querypb.BindVariable{}) 261 require.NoError(t, err) 262 require.False(t, session.InReservedConn()) 263 264 _, err = executor.Execute(context.Background(), "TestSelect", session, "select 1 from information_schema.table", map[string]*querypb.BindVariable{}) 265 require.NoError(t, err) 266 267 _, err = executor.Execute(context.Background(), "TestCommit", session, "commit", map[string]*querypb.BindVariable{}) 268 require.NoError(t, err) 269 require.False(t, session.InReservedConn()) 270 271 require.Zero(t, session.ShardSessions) 272 273 wantQueries := []*querypb.BoundQuery{ 274 {Sql: "select :vtg1 from information_schema.`table`", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 275 {Sql: "select @@sql_mode orig, 'only_full_group_by' new"}, 276 {Sql: "select /*+ SET_VAR(sql_mode = 'only_full_group_by') */ :vtg1 from information_schema.`table`", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 277 } 278 279 utils.MustMatch(t, wantQueries, sbc1.Queries) 280 } 281 282 func TestSetSystemVariables(t *testing.T) { 283 executor, _, _, lookup := createExecutorEnv() 284 executor.normalize = true 285 286 sqlparser.SetParserVersion("80001") 287 288 session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded, SystemVariables: map[string]string{}}) 289 290 // Set @@sql_mode and execute a select statement. We should have SET_VAR in the select statement 291 292 lookup.SetResults([]*sqltypes.Result{{ 293 Fields: []*querypb.Field{ 294 {Name: "orig", Type: sqltypes.VarChar}, 295 {Name: "new", Type: sqltypes.VarChar}, 296 }, 297 Rows: [][]sqltypes.Value{{ 298 sqltypes.NewVarChar(""), 299 sqltypes.NewVarChar("only_full_group_by"), 300 }}, 301 }}) 302 _, err := executor.Execute(context.Background(), "TestSetStmt", session, "set @@sql_mode = only_full_group_by", map[string]*querypb.BindVariable{}) 303 require.NoError(t, err) 304 305 _, err = executor.Execute(context.Background(), "TestSelect", session, "select 1 from information_schema.table", map[string]*querypb.BindVariable{}) 306 require.NoError(t, err) 307 require.False(t, session.InReservedConn()) 308 wantQueries := []*querypb.BoundQuery{ 309 {Sql: "select @@sql_mode orig, 'only_full_group_by' new"}, 310 {Sql: "select /*+ SET_VAR(sql_mode = 'only_full_group_by') */ :vtg1 from information_schema.`table`", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 311 } 312 utils.MustMatch(t, wantQueries, lookup.Queries) 313 lookup.Queries = nil 314 315 // Execute a select with a comment that needs a query hint 316 317 _, err = executor.Execute(context.Background(), "TestSelect", session, "select /* comment */ 1 from information_schema.table", map[string]*querypb.BindVariable{}) 318 require.NoError(t, err) 319 require.False(t, session.InReservedConn()) 320 wantQueries = []*querypb.BoundQuery{ 321 {Sql: "select /*+ SET_VAR(sql_mode = 'only_full_group_by') */ /* comment */ :vtg1 from information_schema.`table`", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 322 } 323 utils.MustMatch(t, wantQueries, lookup.Queries) 324 lookup.Queries = nil 325 326 lookup.SetResults([]*sqltypes.Result{{ 327 Fields: []*querypb.Field{ 328 {Name: "sql_safe_updates", Type: sqltypes.VarChar}, 329 }, 330 Rows: [][]sqltypes.Value{{ 331 sqltypes.NewVarChar("0"), 332 }}, 333 }}) 334 _, err = executor.Execute(context.Background(), "TestSetStmt", session, "set @@sql_safe_updates = 0", map[string]*querypb.BindVariable{}) 335 require.NoError(t, err) 336 require.False(t, session.InReservedConn()) 337 wantQueries = []*querypb.BoundQuery{ 338 {Sql: "select 0 from dual where @@sql_safe_updates != 0"}, 339 } 340 utils.MustMatch(t, wantQueries, lookup.Queries) 341 lookup.Queries = nil 342 343 _, err = executor.Execute(context.Background(), "TestSetStmt", session, "set @var = @@sql_mode", map[string]*querypb.BindVariable{}) 344 require.NoError(t, err) 345 require.False(t, session.InReservedConn()) 346 require.Nil(t, lookup.Queries) 347 require.Equal(t, "only_full_group_by", string(session.UserDefinedVariables["var"].GetValue())) 348 349 lookup.SetResults([]*sqltypes.Result{{ 350 Fields: []*querypb.Field{ 351 {Name: "max_tmp_tables", Type: sqltypes.VarChar}, 352 }, 353 Rows: [][]sqltypes.Value{{ 354 sqltypes.NewVarChar("4"), 355 }}, 356 }}) 357 _, err = executor.Execute(context.Background(), "TestSetStmt", session, "set @x = @@sql_mode, @y = @@max_tmp_tables", map[string]*querypb.BindVariable{}) 358 require.NoError(t, err) 359 require.False(t, session.InReservedConn()) 360 wantQueries = []*querypb.BoundQuery{ 361 {Sql: "select @@max_tmp_tables from dual", BindVariables: map[string]*querypb.BindVariable{"__vtsql_mode": sqltypes.StringBindVariable("only_full_group_by")}}, 362 } 363 utils.MustMatch(t, wantQueries, lookup.Queries) 364 require.Equal(t, "only_full_group_by", string(session.UserDefinedVariables["var"].GetValue())) 365 require.Equal(t, "only_full_group_by", string(session.UserDefinedVariables["x"].GetValue())) 366 require.Equal(t, "4", string(session.UserDefinedVariables["y"].GetValue())) 367 lookup.Queries = nil 368 369 // Set system variable that is not supported by SET_VAR 370 // We expect the next select to not have any SET_VAR query hint, instead it will use set statements 371 372 lookup.SetResults([]*sqltypes.Result{{ 373 Fields: []*querypb.Field{ 374 {Name: "max_tmp_tables", Type: sqltypes.VarChar}, 375 }, 376 Rows: [][]sqltypes.Value{{ 377 sqltypes.NewVarChar("1"), 378 }}, 379 }}) 380 _, err = executor.Execute(context.Background(), "TestSetStmt", session, "set @@max_tmp_tables = 1", map[string]*querypb.BindVariable{}) 381 require.NoError(t, err) 382 require.True(t, session.InReservedConn()) 383 384 _, err = executor.Execute(context.Background(), "TestSelect", session, "select 1 from information_schema.table", map[string]*querypb.BindVariable{}) 385 require.NoError(t, err) 386 387 wantQueries = []*querypb.BoundQuery{ 388 {Sql: "select 1 from dual where @@max_tmp_tables != 1"}, 389 {Sql: "set max_tmp_tables = '1', sql_mode = 'only_full_group_by', sql_safe_updates = '0'", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 390 {Sql: "select :vtg1 from information_schema.`table`", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 391 } 392 utils.MustMatch(t, wantQueries, lookup.Queries) 393 } 394 395 func TestSetSystemVariablesWithReservedConnection(t *testing.T) { 396 executor, sbc1, _, _ := createExecutorEnv() 397 executor.normalize = true 398 399 session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) 400 401 sbc1.SetResults([]*sqltypes.Result{{ 402 Fields: []*querypb.Field{ 403 {Name: "orig", Type: sqltypes.VarChar}, 404 {Name: "new", Type: sqltypes.VarChar}, 405 }, 406 Rows: [][]sqltypes.Value{{ 407 sqltypes.NewVarChar("only_full_group_by"), 408 sqltypes.NewVarChar(""), 409 }}, 410 }}) 411 _, err := executor.Execute(context.Background(), "TestSetStmt", session, "set @@sql_mode = ''", map[string]*querypb.BindVariable{}) 412 require.NoError(t, err) 413 414 _, err = executor.Execute(context.Background(), "TestSelect", session, "select age, city from user group by age", map[string]*querypb.BindVariable{}) 415 require.NoError(t, err) 416 require.True(t, session.InReservedConn()) 417 wantQueries := []*querypb.BoundQuery{ 418 {Sql: "select @@sql_mode orig, '' new"}, 419 {Sql: "set sql_mode = ''"}, 420 {Sql: "select age, city, weight_string(age) from `user` group by age, weight_string(age) order by age asc"}, 421 } 422 utils.MustMatch(t, wantQueries, sbc1.Queries) 423 424 _, err = executor.Execute(context.Background(), "TestSelect", session, "select age, city+1 from user group by age", map[string]*querypb.BindVariable{}) 425 require.NoError(t, err) 426 require.True(t, session.InReservedConn()) 427 wantQueries = []*querypb.BoundQuery{ 428 {Sql: "select @@sql_mode orig, '' new"}, 429 {Sql: "set sql_mode = ''"}, 430 {Sql: "select age, city, weight_string(age) from `user` group by age, weight_string(age) order by age asc"}, 431 {Sql: "select age, city + :vtg1, weight_string(age) from `user` group by age, weight_string(age) order by age asc", BindVariables: map[string]*querypb.BindVariable{"vtg1": {Type: sqltypes.Int64, Value: []byte("1")}}}, 432 } 433 utils.MustMatch(t, wantQueries, sbc1.Queries) 434 require.Equal(t, "''", session.SystemVariables["sql_mode"]) 435 sbc1.Queries = nil 436 } 437 438 func TestCreateTableValidTimestamp(t *testing.T) { 439 executor, sbc1, _, _ := createExecutorEnv() 440 executor.normalize = true 441 442 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor", SystemVariables: map[string]string{"sql_mode": "ALLOW_INVALID_DATES"}}) 443 444 query := "create table aa(t timestamp default 0)" 445 _, err := executor.Execute(context.Background(), "TestSelect", session, query, map[string]*querypb.BindVariable{}) 446 require.NoError(t, err) 447 require.True(t, session.InReservedConn()) 448 449 wantQueries := []*querypb.BoundQuery{ 450 {Sql: "set sql_mode = ALLOW_INVALID_DATES", BindVariables: map[string]*querypb.BindVariable{}}, 451 {Sql: "create table aa (\n\tt timestamp default 0\n)", BindVariables: map[string]*querypb.BindVariable{}}, 452 } 453 454 utils.MustMatch(t, wantQueries, sbc1.Queries) 455 } 456 457 func TestGen4SelectDBA(t *testing.T) { 458 executor, sbc1, _, _ := createExecutorEnv() 459 executor.normalize = true 460 executor.pv = querypb.ExecuteOptions_Gen4 461 462 query := "select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS" 463 _, err := executor.Execute(context.Background(), "TestSelectDBA", 464 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 465 query, map[string]*querypb.BindVariable{}, 466 ) 467 require.NoError(t, err) 468 expected := "select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_TYPE, `ENFORCED` from INFORMATION_SCHEMA.TABLE_CONSTRAINTS" 469 wantQueries := []*querypb.BoundQuery{{Sql: expected, BindVariables: map[string]*querypb.BindVariable{}}} 470 utils.MustMatch(t, wantQueries, sbc1.Queries) 471 472 sbc1.Queries = nil 473 query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES ist WHERE ist.table_schema = 'performance_schema' AND ist.table_name = 'foo'" 474 _, err = executor.Execute(context.Background(), "TestSelectDBA", 475 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 476 query, map[string]*querypb.BindVariable{}, 477 ) 478 require.NoError(t, err) 479 wantQueries = []*querypb.BoundQuery{{Sql: "select count(*) from INFORMATION_SCHEMA.`TABLES` as ist where ist.table_schema = :__vtschemaname and ist.table_name = :ist_table_name1", 480 BindVariables: map[string]*querypb.BindVariable{ 481 "ist_table_schema": sqltypes.StringBindVariable("performance_schema"), 482 "__vtschemaname": sqltypes.StringBindVariable("performance_schema"), 483 "ist_table_name": sqltypes.StringBindVariable("foo"), 484 "ist_table_name1": sqltypes.StringBindVariable("foo"), 485 }}} 486 utils.MustMatch(t, wantQueries, sbc1.Queries) 487 488 sbc1.Queries = nil 489 query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks' and table_name = 'user'" 490 _, err = executor.Execute(context.Background(), "TestSelectDBA", 491 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 492 query, map[string]*querypb.BindVariable{}, 493 ) 494 require.NoError(t, err) 495 wantQueries = []*querypb.BoundQuery{{Sql: "select :vtg1 from information_schema.table_constraints where constraint_schema = :__vtschemaname and table_name = :table_name1", 496 BindVariables: map[string]*querypb.BindVariable{ 497 "vtg1": sqltypes.Int64BindVariable(1), 498 "constraint_schema": sqltypes.StringBindVariable("vt_ks"), 499 "table_name": sqltypes.StringBindVariable("user"), 500 "__vtschemaname": sqltypes.StringBindVariable("vt_ks"), 501 "table_name1": sqltypes.StringBindVariable("user"), 502 }}} 503 utils.MustMatch(t, wantQueries, sbc1.Queries) 504 505 sbc1.Queries = nil 506 query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks'" 507 _, err = executor.Execute(context.Background(), "TestSelectDBA", 508 NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), 509 query, map[string]*querypb.BindVariable{}, 510 ) 511 require.NoError(t, err) 512 wantQueries = []*querypb.BoundQuery{{Sql: "select :vtg1 from information_schema.table_constraints where constraint_schema = :__vtschemaname", 513 BindVariables: map[string]*querypb.BindVariable{ 514 "vtg1": sqltypes.Int64BindVariable(1), 515 "constraint_schema": sqltypes.StringBindVariable("vt_ks"), 516 "__vtschemaname": sqltypes.StringBindVariable("vt_ks"), 517 }}} 518 utils.MustMatch(t, wantQueries, sbc1.Queries) 519 520 sbc1.Queries = nil 521 query = "select t.table_schema,t.table_name,c.column_name,c.column_type from tables t join columns c on c.table_schema = t.table_schema and c.table_name = t.table_name where t.table_schema = 'TestExecutor' and c.table_schema = 'TestExecutor' order by t.table_schema,t.table_name,c.column_name" 522 _, err = executor.Execute(context.Background(), "TestSelectDBA", 523 NewSafeSession(&vtgatepb.Session{TargetString: "information_schema"}), 524 query, map[string]*querypb.BindVariable{}, 525 ) 526 require.NoError(t, err) 527 wantQueries = []*querypb.BoundQuery{{Sql: "select t.table_schema, t.table_name, c.column_name, c.column_type from information_schema.`tables` as t, information_schema.`columns` as c where t.table_schema = :__vtschemaname and c.table_schema = :__vtschemaname and c.table_schema = t.table_schema and c.table_name = t.table_name order by t.table_schema asc, t.table_name asc, c.column_name asc", 528 BindVariables: map[string]*querypb.BindVariable{ 529 "t_table_schema": sqltypes.StringBindVariable("TestExecutor"), 530 "__replacevtschemaname": sqltypes.Int64BindVariable(1), 531 }}} 532 utils.MustMatch(t, wantQueries, sbc1.Queries) 533 } 534 535 func TestUnsharded(t *testing.T) { 536 executor, _, _, sbclookup := createExecutorEnv() 537 538 _, err := executorExec(executor, "select id from music_user_map where id = 1", nil) 539 require.NoError(t, err) 540 wantQueries := []*querypb.BoundQuery{{ 541 Sql: "select id from music_user_map where id = 1", 542 BindVariables: map[string]*querypb.BindVariable{}, 543 }} 544 utils.MustMatch(t, wantQueries, sbclookup.Queries) 545 } 546 547 func TestUnshardedComments(t *testing.T) { 548 executor, _, _, sbclookup := createExecutorEnv() 549 550 _, err := executorExec(executor, "/* leading */ select id from music_user_map where id = 1 /* trailing */", nil) 551 require.NoError(t, err) 552 wantQueries := []*querypb.BoundQuery{{ 553 Sql: "/* leading */ select id from music_user_map where id = 1 /* trailing */", 554 BindVariables: map[string]*querypb.BindVariable{}, 555 }} 556 utils.MustMatch(t, wantQueries, sbclookup.Queries) 557 558 _, err = executorExec(executor, "update music_user_map set id = 1 /* trailing */", nil) 559 require.NoError(t, err) 560 wantQueries = []*querypb.BoundQuery{{ 561 Sql: "/* leading */ select id from music_user_map where id = 1 /* trailing */", 562 BindVariables: map[string]*querypb.BindVariable{}, 563 }, { 564 Sql: "update music_user_map set id = 1 /* trailing */", 565 BindVariables: map[string]*querypb.BindVariable{}, 566 }} 567 assertQueries(t, sbclookup, wantQueries) 568 569 sbclookup.Queries = nil 570 _, err = executorExec(executor, "delete from music_user_map /* trailing */", nil) 571 require.NoError(t, err) 572 wantQueries = []*querypb.BoundQuery{{ 573 Sql: "delete from music_user_map /* trailing */", 574 BindVariables: map[string]*querypb.BindVariable{}, 575 }} 576 assertQueries(t, sbclookup, wantQueries) 577 578 sbclookup.Queries = nil 579 _, err = executorExec(executor, "insert into music_user_map values (1) /* trailing */", nil) 580 require.NoError(t, err) 581 wantQueries = []*querypb.BoundQuery{{ 582 Sql: "insert into music_user_map values (1) /* trailing */", 583 BindVariables: map[string]*querypb.BindVariable{}, 584 }} 585 assertQueries(t, sbclookup, wantQueries) 586 } 587 588 func TestStreamUnsharded(t *testing.T) { 589 executor, _, _, _ := createExecutorEnv() 590 logChan := QueryLogger.Subscribe("Test") 591 defer QueryLogger.Unsubscribe(logChan) 592 593 sql := "select id from music_user_map where id = 1" 594 result, err := executorStream(executor, sql) 595 require.NoError(t, err) 596 wantResult := sandboxconn.StreamRowResult 597 if !result.Equal(wantResult) { 598 diff := cmp.Diff(wantResult, result) 599 t.Errorf("result: %+v, want %+v\ndiff: %s", result, wantResult, diff) 600 } 601 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", sql, 1) 602 } 603 604 func TestStreamBuffering(t *testing.T) { 605 executor, _, _, sbclookup := createExecutorEnv() 606 607 // This test is similar to TestStreamUnsharded except that it returns a Result > 10 bytes, 608 // such that the splitting of the Result into multiple Result responses gets tested. 609 sbclookup.SetResults([]*sqltypes.Result{{ 610 Fields: []*querypb.Field{ 611 {Name: "id", Type: sqltypes.Int32}, 612 {Name: "col", Type: sqltypes.VarChar}, 613 }, 614 Rows: [][]sqltypes.Value{{ 615 sqltypes.NewInt32(1), 616 sqltypes.NewVarChar("01234567890123456789"), 617 }, { 618 sqltypes.NewInt32(2), 619 sqltypes.NewVarChar("12345678901234567890"), 620 }}, 621 }}) 622 623 var results []*sqltypes.Result 624 err := executor.StreamExecute( 625 context.Background(), 626 "TestStreamBuffering", 627 NewSafeSession(primarySession), 628 "select id from music_user_map where id = 1", 629 nil, 630 func(qr *sqltypes.Result) error { 631 results = append(results, qr) 632 return nil 633 }, 634 ) 635 require.NoError(t, err) 636 wantResults := []*sqltypes.Result{{ 637 Fields: []*querypb.Field{ 638 {Name: "id", Type: sqltypes.Int32}, 639 {Name: "col", Type: sqltypes.VarChar}, 640 }, 641 }, { 642 Rows: [][]sqltypes.Value{{ 643 sqltypes.NewInt32(1), 644 sqltypes.NewVarChar("01234567890123456789"), 645 }}, 646 }, { 647 Rows: [][]sqltypes.Value{{ 648 sqltypes.NewInt32(2), 649 sqltypes.NewVarChar("12345678901234567890"), 650 }}, 651 }} 652 utils.MustMatch(t, wantResults, results) 653 } 654 655 func TestStreamLimitOffset(t *testing.T) { 656 executor, sbc1, sbc2, _ := createExecutorEnv() 657 658 // This test is similar to TestStreamUnsharded except that it returns a Result > 10 bytes, 659 // such that the splitting of the Result into multiple Result responses gets tested. 660 sbc1.SetResults([]*sqltypes.Result{{ 661 Fields: []*querypb.Field{ 662 {Name: "id", Type: sqltypes.Int32}, 663 {Name: "textcol", Type: sqltypes.VarChar}, 664 {Name: "weight_string(id)", Type: sqltypes.VarBinary}, 665 }, 666 Rows: [][]sqltypes.Value{{ 667 sqltypes.NewInt32(1), 668 sqltypes.NewVarChar("1234"), 669 sqltypes.NULL, 670 }, { 671 sqltypes.NewInt32(4), 672 sqltypes.NewVarChar("4567"), 673 sqltypes.NULL, 674 }}, 675 }}) 676 677 sbc2.SetResults([]*sqltypes.Result{{ 678 Fields: []*querypb.Field{ 679 {Name: "id", Type: sqltypes.Int32}, 680 {Name: "textcol", Type: sqltypes.VarChar}, 681 {Name: "weight_string(id)", Type: sqltypes.VarBinary}, 682 }, 683 Rows: [][]sqltypes.Value{{ 684 sqltypes.NewInt32(2), 685 sqltypes.NewVarChar("2345"), 686 sqltypes.NULL, 687 }}, 688 }}) 689 690 results := make(chan *sqltypes.Result, 10) 691 err := executor.StreamExecute( 692 context.Background(), 693 "TestStreamLimitOffset", 694 NewSafeSession(primarySession), 695 "select id, textcol from user order by id limit 2 offset 2", 696 nil, 697 func(qr *sqltypes.Result) error { 698 results <- qr 699 return nil 700 }, 701 ) 702 close(results) 703 require.NoError(t, err) 704 wantResult := &sqltypes.Result{ 705 Fields: []*querypb.Field{ 706 {Name: "id", Type: sqltypes.Int32}, 707 {Name: "textcol", Type: sqltypes.VarChar}, 708 }, 709 710 Rows: [][]sqltypes.Value{{ 711 sqltypes.NewInt32(1), 712 sqltypes.NewVarChar("1234"), 713 }, { 714 sqltypes.NewInt32(1), 715 sqltypes.NewVarChar("foo"), 716 }}, 717 } 718 var gotResults []*sqltypes.Result 719 for r := range results { 720 gotResults = append(gotResults, r) 721 } 722 res := gotResults[0] 723 for i := 1; i < len(gotResults); i++ { 724 res.Rows = append(res.Rows, gotResults[i].Rows...) 725 } 726 utils.MustMatch(t, wantResult, res, "") 727 } 728 729 func TestSelectLastInsertId(t *testing.T) { 730 executor, _, _, _ := createExecutorEnv() 731 primarySession.LastInsertId = 52 732 executor.normalize = true 733 logChan := QueryLogger.Subscribe("Test") 734 defer QueryLogger.Unsubscribe(logChan) 735 736 sql := "select last_insert_id()" 737 result, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) 738 wantResult := &sqltypes.Result{ 739 Fields: []*querypb.Field{ 740 {Name: "last_insert_id()", Type: sqltypes.Uint64}, 741 }, 742 Rows: [][]sqltypes.Value{{ 743 sqltypes.NewUint64(52), 744 }}, 745 } 746 require.NoError(t, err) 747 utils.MustMatch(t, wantResult, result, "Mismatch") 748 } 749 750 func TestSelectSystemVariables(t *testing.T) { 751 executor, _, _, _ := createExecutorEnv() 752 primarySession.ReadAfterWrite = &vtgatepb.ReadAfterWrite{ 753 ReadAfterWriteGtid: "a fine gtid", 754 ReadAfterWriteTimeout: 13, 755 SessionTrackGtids: true, 756 } 757 executor.normalize = true 758 logChan := QueryLogger.Subscribe("Test") 759 defer QueryLogger.Unsubscribe(logChan) 760 761 sql := "select @@autocommit, @@client_found_rows, @@skip_query_plan_cache, @@enable_system_settings, " + 762 "@@sql_select_limit, @@transaction_mode, @@workload, @@read_after_write_gtid, " + 763 "@@read_after_write_timeout, @@session_track_gtids, @@ddl_strategy, @@socket, @@query_timeout" 764 765 result, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) 766 wantResult := &sqltypes.Result{ 767 Fields: []*querypb.Field{ 768 {Name: "@@autocommit", Type: sqltypes.Int64}, 769 {Name: "@@client_found_rows", Type: sqltypes.Int64}, 770 {Name: "@@skip_query_plan_cache", Type: sqltypes.Int64}, 771 {Name: "@@enable_system_settings", Type: sqltypes.Int64}, 772 {Name: "@@sql_select_limit", Type: sqltypes.Int64}, 773 {Name: "@@transaction_mode", Type: sqltypes.VarChar}, 774 {Name: "@@workload", Type: sqltypes.VarChar}, 775 {Name: "@@read_after_write_gtid", Type: sqltypes.VarChar}, 776 {Name: "@@read_after_write_timeout", Type: sqltypes.Float64}, 777 {Name: "@@session_track_gtids", Type: sqltypes.VarChar}, 778 {Name: "@@ddl_strategy", Type: sqltypes.VarChar}, 779 {Name: "@@socket", Type: sqltypes.VarChar}, 780 {Name: "@@query_timeout", Type: sqltypes.Int64}, 781 }, 782 Rows: [][]sqltypes.Value{{ 783 // the following are the uninitialised session values 784 sqltypes.NewInt64(0), 785 sqltypes.NewInt64(0), 786 sqltypes.NewInt64(0), 787 sqltypes.NewInt64(0), 788 sqltypes.NewInt64(0), 789 sqltypes.NewVarChar("UNSPECIFIED"), 790 sqltypes.NewVarChar(""), 791 // these have been set at the beginning of the test 792 sqltypes.NewVarChar("a fine gtid"), 793 sqltypes.NewFloat64(13), 794 sqltypes.NewVarChar("own_gtid"), 795 sqltypes.NewVarChar(""), 796 sqltypes.NewVarChar(""), 797 sqltypes.NewInt64(0), 798 }}, 799 } 800 require.NoError(t, err) 801 utils.MustMatch(t, wantResult, result, "Mismatch") 802 } 803 804 func TestSelectInitializedVitessAwareVariable(t *testing.T) { 805 executor, _, _, _ := createExecutorEnv() 806 executor.normalize = true 807 logChan := QueryLogger.Subscribe("Test") 808 defer QueryLogger.Unsubscribe(logChan) 809 810 primarySession.Autocommit = true 811 primarySession.EnableSystemSettings = true 812 primarySession.QueryTimeout = 75 813 814 defer func() { 815 primarySession.Autocommit = false 816 primarySession.EnableSystemSettings = false 817 primarySession.QueryTimeout = 0 818 }() 819 820 sql := "select @@autocommit, @@enable_system_settings, @@query_timeout" 821 822 result, err := executorExec(executor, sql, nil) 823 wantResult := &sqltypes.Result{ 824 Fields: []*querypb.Field{ 825 {Name: "@@autocommit", Type: sqltypes.Int64}, 826 {Name: "@@enable_system_settings", Type: sqltypes.Int64}, 827 {Name: "@@query_timeout", Type: sqltypes.Int64}, 828 }, 829 Rows: [][]sqltypes.Value{{ 830 sqltypes.NewInt64(1), 831 sqltypes.NewInt64(1), 832 sqltypes.NewInt64(75), 833 }}, 834 } 835 require.NoError(t, err) 836 utils.MustMatch(t, wantResult, result, "Mismatch") 837 } 838 839 func TestSelectUserDefinedVariable(t *testing.T) { 840 executor, _, _, _ := createExecutorEnv() 841 executor.normalize = true 842 logChan := QueryLogger.Subscribe("Test") 843 defer QueryLogger.Unsubscribe(logChan) 844 845 sql := "select @foo" 846 result, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) 847 require.NoError(t, err) 848 wantResult := &sqltypes.Result{ 849 Fields: []*querypb.Field{ 850 {Name: "@foo", Type: sqltypes.Null}, 851 }, 852 Rows: [][]sqltypes.Value{{ 853 sqltypes.NULL, 854 }}, 855 } 856 utils.MustMatch(t, wantResult, result, "Mismatch") 857 858 primarySession = &vtgatepb.Session{UserDefinedVariables: createMap([]string{"foo"}, []any{"bar"})} 859 result, err = executorExec(executor, sql, map[string]*querypb.BindVariable{}) 860 require.NoError(t, err) 861 wantResult = &sqltypes.Result{ 862 Fields: []*querypb.Field{ 863 {Name: "@foo", Type: sqltypes.VarChar}, 864 }, 865 Rows: [][]sqltypes.Value{{ 866 sqltypes.NewVarChar("bar"), 867 }}, 868 } 869 utils.MustMatch(t, wantResult, result, "Mismatch") 870 } 871 872 func TestFoundRows(t *testing.T) { 873 executor, _, _, _ := createExecutorEnv() 874 executor.normalize = true 875 logChan := QueryLogger.Subscribe("Test") 876 defer QueryLogger.Unsubscribe(logChan) 877 878 // run this extra query so we can assert on the number of rows found 879 _, err := executorExec(executor, "select 42", map[string]*querypb.BindVariable{}) 880 require.NoError(t, err) 881 882 sql := "select found_rows()" 883 result, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) 884 wantResult := &sqltypes.Result{ 885 Fields: []*querypb.Field{ 886 {Name: "found_rows()", Type: sqltypes.Int64}, 887 }, 888 Rows: [][]sqltypes.Value{{ 889 sqltypes.NewInt64(1), 890 }}, 891 } 892 require.NoError(t, err) 893 utils.MustMatch(t, wantResult, result, "Mismatch") 894 } 895 896 func TestRowCount(t *testing.T) { 897 executor, _, _, _ := createExecutorEnv() 898 executor.normalize = true 899 logChan := QueryLogger.Subscribe("Test") 900 defer QueryLogger.Unsubscribe(logChan) 901 902 _, err := executorExec(executor, "select 42", map[string]*querypb.BindVariable{}) 903 require.NoError(t, err) 904 testRowCount(t, executor, -1) 905 906 _, err = executorExec(executor, "delete from user where id in (42, 24)", map[string]*querypb.BindVariable{}) 907 require.NoError(t, err) 908 testRowCount(t, executor, 2) 909 } 910 911 func testRowCount(t *testing.T, executor *Executor, wantRowCount int64) { 912 t.Helper() 913 result, err := executorExec(executor, "select row_count()", map[string]*querypb.BindVariable{}) 914 wantResult := &sqltypes.Result{ 915 Fields: []*querypb.Field{ 916 {Name: "row_count()", Type: sqltypes.Int64}, 917 }, 918 Rows: [][]sqltypes.Value{{ 919 sqltypes.NewInt64(wantRowCount), 920 }}, 921 } 922 require.NoError(t, err) 923 utils.MustMatch(t, wantResult, result, "Mismatch") 924 } 925 926 func TestSelectLastInsertIdInUnion(t *testing.T) { 927 executor, sbc1, _, _ := createExecutorEnv() 928 executor.normalize = true 929 primarySession.LastInsertId = 52 930 931 result1 := []*sqltypes.Result{{ 932 Fields: []*querypb.Field{ 933 {Name: "id", Type: sqltypes.Int32}, 934 }, 935 InsertID: 0, 936 Rows: [][]sqltypes.Value{{ 937 sqltypes.NewInt32(52), 938 }}, 939 }} 940 sbc1.SetResults(result1) 941 942 sql := "select last_insert_id() as id union select last_insert_id() as id" 943 got, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) 944 require.NoError(t, err) 945 wantResult := &sqltypes.Result{ 946 Fields: []*querypb.Field{ 947 {Name: "id", Type: sqltypes.Int32}, 948 }, 949 Rows: [][]sqltypes.Value{{ 950 sqltypes.NewInt32(52), 951 }}, 952 } 953 utils.MustMatch(t, wantResult, got, "mismatch") 954 } 955 956 func TestSelectLastInsertIdInWhere(t *testing.T) { 957 executor, _, _, lookup := createExecutorEnv() 958 executor.normalize = true 959 logChan := QueryLogger.Subscribe("Test") 960 defer QueryLogger.Unsubscribe(logChan) 961 962 sql := "select id from music_user_map where id = last_insert_id()" 963 _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) 964 require.NoError(t, err) 965 wantQueries := []*querypb.BoundQuery{{ 966 Sql: "select id from music_user_map where id = :__lastInsertId", 967 BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, 968 }} 969 970 assert.Equal(t, wantQueries, lookup.Queries) 971 } 972 973 func TestLastInsertIDInVirtualTable(t *testing.T) { 974 executor, sbc1, _, _ := createExecutorEnv() 975 executor.normalize = true 976 result1 := []*sqltypes.Result{{ 977 Fields: []*querypb.Field{ 978 {Name: "id", Type: sqltypes.Int32}, 979 {Name: "col", Type: sqltypes.Int32}, 980 }, 981 InsertID: 0, 982 Rows: [][]sqltypes.Value{{ 983 sqltypes.NewInt32(1), 984 sqltypes.NewInt32(3), 985 }}, 986 }} 987 sbc1.SetResults(result1) 988 _, err := executorExec(executor, "select * from (select last_insert_id()) as t", nil) 989 require.NoError(t, err) 990 wantQueries := []*querypb.BoundQuery{{ 991 Sql: "select * from (select :__lastInsertId as `last_insert_id()` from dual) as t", 992 BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, 993 }} 994 995 assert.Equal(t, wantQueries, sbc1.Queries) 996 } 997 998 func TestLastInsertIDInSubQueryExpression(t *testing.T) { 999 executor, sbc1, sbc2, _ := createExecutorEnv() 1000 executor.normalize = true 1001 primarySession.LastInsertId = 12345 1002 defer func() { 1003 // clean up global state 1004 primarySession.LastInsertId = 0 1005 }() 1006 rs, err := executorExec(executor, "select (select last_insert_id()) as x", nil) 1007 require.NoError(t, err) 1008 wantResult := &sqltypes.Result{ 1009 Fields: []*querypb.Field{ 1010 {Name: "x", Type: sqltypes.Uint64}, 1011 }, 1012 Rows: [][]sqltypes.Value{{ 1013 sqltypes.NewUint64(12345), 1014 }}, 1015 } 1016 utils.MustMatch(t, rs, wantResult, "Mismatch") 1017 1018 // the query will get rewritten into a simpler query that can be run entirely on the vtgate 1019 assert.Empty(t, sbc1.Queries) 1020 assert.Empty(t, sbc2.Queries) 1021 } 1022 1023 func TestSelectDatabase(t *testing.T) { 1024 executor, _, _, _ := createExecutorEnv() 1025 executor.normalize = true 1026 sql := "select database()" 1027 newSession := proto.Clone(primarySession).(*vtgatepb.Session) 1028 session := NewSafeSession(newSession) 1029 session.TargetString = "TestExecutor@primary" 1030 result, err := executor.Execute( 1031 context.Background(), 1032 "TestExecute", 1033 session, 1034 sql, 1035 map[string]*querypb.BindVariable{}) 1036 wantResult := &sqltypes.Result{ 1037 Fields: []*querypb.Field{ 1038 {Name: "database()", Type: sqltypes.VarChar}, 1039 }, 1040 Rows: [][]sqltypes.Value{{ 1041 sqltypes.NewVarChar("TestExecutor@primary"), 1042 }}, 1043 } 1044 require.NoError(t, err) 1045 utils.MustMatch(t, wantResult, result, "Mismatch") 1046 1047 } 1048 1049 func TestSelectBindvars(t *testing.T) { 1050 executor, sbc1, sbc2, lookup := createExecutorEnv() 1051 logChan := QueryLogger.Subscribe("Test") 1052 defer QueryLogger.Unsubscribe(logChan) 1053 1054 lookup.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( 1055 sqltypes.MakeTestFields("b|a", "varbinary|varbinary"), 1056 "foo1|1", 1057 ), sqltypes.MakeTestResult( 1058 sqltypes.MakeTestFields("b|a", "varbinary|varbinary"), 1059 "foo2|1", 1060 )}) 1061 1062 sql := "select id from user where id = :id" 1063 _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{ 1064 "id": sqltypes.Int64BindVariable(1), 1065 }) 1066 require.NoError(t, err) 1067 wantQueries := []*querypb.BoundQuery{{ 1068 Sql: "select id from `user` where id = :id", 1069 BindVariables: map[string]*querypb.BindVariable{"id": sqltypes.Int64BindVariable(1)}, 1070 }} 1071 utils.MustMatch(t, sbc1.Queries, wantQueries) 1072 assert.Empty(t, sbc2.Queries) 1073 sbc1.Queries = nil 1074 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 1) 1075 1076 // Test with StringBindVariable 1077 sql = "select id from user where name in (:name1, :name2)" 1078 _, err = executorExec(executor, sql, map[string]*querypb.BindVariable{ 1079 "name1": sqltypes.StringBindVariable("foo1"), 1080 "name2": sqltypes.StringBindVariable("foo2"), 1081 }) 1082 require.NoError(t, err) 1083 wantQueries = []*querypb.BoundQuery{{ 1084 Sql: "select id from `user` where `name` in ::__vals", 1085 BindVariables: map[string]*querypb.BindVariable{ 1086 "name1": sqltypes.StringBindVariable("foo1"), 1087 "name2": sqltypes.StringBindVariable("foo2"), 1088 "__vals": sqltypes.TestBindVariable([]any{"foo1", "foo2"}), 1089 }, 1090 }} 1091 utils.MustMatch(t, wantQueries, sbc1.Queries) 1092 sbc1.Queries = nil 1093 testQueryLog(t, logChan, "VindexLookup", "SELECT", "select name, user_id from name_user_map where name in ::name", 1) 1094 testQueryLog(t, logChan, "VindexLookup", "SELECT", "select name, user_id from name_user_map where name in ::name", 1) 1095 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 1) 1096 1097 // Test with BytesBindVariable 1098 sql = "select id from user where name in (:name1, :name2)" 1099 _, err = executorExec(executor, sql, map[string]*querypb.BindVariable{ 1100 "name1": sqltypes.BytesBindVariable([]byte("foo1")), 1101 "name2": sqltypes.BytesBindVariable([]byte("foo2")), 1102 }) 1103 require.NoError(t, err) 1104 wantQueries = []*querypb.BoundQuery{{ 1105 Sql: "select id from `user` where 1 != 1", 1106 BindVariables: map[string]*querypb.BindVariable{ 1107 "name1": sqltypes.BytesBindVariable([]byte("foo1")), 1108 "name2": sqltypes.BytesBindVariable([]byte("foo2")), 1109 }, 1110 }} 1111 utils.MustMatch(t, wantQueries, sbc1.Queries) 1112 testQueryLog(t, logChan, "VindexLookup", "SELECT", "select name, user_id from name_user_map where name in ::name", 1) 1113 testQueryLog(t, logChan, "VindexLookup", "SELECT", "select name, user_id from name_user_map where name in ::name", 1) 1114 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 1) 1115 1116 // Test no match in the lookup vindex 1117 sbc1.Queries = nil 1118 lookup.Queries = nil 1119 lookup.SetResults([]*sqltypes.Result{{ 1120 Fields: []*querypb.Field{ 1121 {Name: "user_id", Type: sqltypes.Int32}, 1122 }, 1123 RowsAffected: 0, 1124 InsertID: 0, 1125 Rows: [][]sqltypes.Value{}, 1126 }}) 1127 1128 sql = "select id from user where name = :name" 1129 _, err = executorExec(executor, sql, map[string]*querypb.BindVariable{ 1130 "name": sqltypes.StringBindVariable("nonexistent"), 1131 }) 1132 require.NoError(t, err) 1133 1134 // When there are no matching rows in the vindex, vtgate still needs the field info 1135 wantQueries = []*querypb.BoundQuery{{ 1136 Sql: "select id from `user` where 1 != 1", 1137 BindVariables: map[string]*querypb.BindVariable{ 1138 "name": sqltypes.StringBindVariable("nonexistent"), 1139 }, 1140 }} 1141 utils.MustMatch(t, wantQueries, sbc1.Queries) 1142 1143 vars, err := sqltypes.BuildBindVariable([]any{sqltypes.NewVarChar("nonexistent")}) 1144 require.NoError(t, err) 1145 wantLookupQueries := []*querypb.BoundQuery{{ 1146 Sql: "select `name`, user_id from name_user_map where `name` in ::name", 1147 BindVariables: map[string]*querypb.BindVariable{ 1148 "name": vars, 1149 }, 1150 }} 1151 1152 utils.MustMatch(t, wantLookupQueries, lookup.Queries) 1153 1154 testQueryLog(t, logChan, "VindexLookup", "SELECT", "select name, user_id from name_user_map where name in ::name", 1) 1155 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 1) 1156 1157 } 1158 1159 func TestSelectEqual(t *testing.T) { 1160 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 1161 1162 _, err := executorExec(executor, "select id from user where id = 1", nil) 1163 require.NoError(t, err) 1164 wantQueries := []*querypb.BoundQuery{{ 1165 Sql: "select id from `user` where id = 1", 1166 BindVariables: map[string]*querypb.BindVariable{}, 1167 }} 1168 utils.MustMatch(t, wantQueries, sbc1.Queries) 1169 if sbc2.Queries != nil { 1170 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 1171 } 1172 sbc1.Queries = nil 1173 1174 _, err = executorExec(executor, "select id from user where id = 3", nil) 1175 require.NoError(t, err) 1176 wantQueries = []*querypb.BoundQuery{{ 1177 Sql: "select id from `user` where id = 3", 1178 BindVariables: map[string]*querypb.BindVariable{}, 1179 }} 1180 utils.MustMatch(t, wantQueries, sbc2.Queries) 1181 if execCount := sbc1.ExecCount.Get(); execCount != 1 { 1182 t.Errorf("sbc1.ExecCount: %v, want 1\n", execCount) 1183 } 1184 if sbc1.Queries != nil { 1185 t.Errorf("sbc1.Queries: %+v, want nil\n", sbc1.Queries) 1186 } 1187 sbc2.Queries = nil 1188 1189 _, err = executorExec(executor, "select id from user where id = '3'", nil) 1190 require.NoError(t, err) 1191 wantQueries = []*querypb.BoundQuery{{ 1192 Sql: "select id from `user` where id = '3'", 1193 BindVariables: map[string]*querypb.BindVariable{}, 1194 }} 1195 utils.MustMatch(t, wantQueries, sbc2.Queries) 1196 if execCount := sbc1.ExecCount.Get(); execCount != 1 { 1197 t.Errorf("sbc1.ExecCount: %v, want 1\n", execCount) 1198 } 1199 if sbc1.Queries != nil { 1200 t.Errorf("sbc1.Queries: %+v, want nil\n", sbc1.Queries) 1201 } 1202 sbc2.Queries = nil 1203 1204 sbclookup.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( 1205 sqltypes.MakeTestFields("b|a", "varbinary|varbinary"), 1206 "foo|1", 1207 )}) 1208 _, err = executorExec(executor, "select id from user where name = 'foo'", nil) 1209 require.NoError(t, err) 1210 wantQueries = []*querypb.BoundQuery{{ 1211 Sql: "select id from `user` where `name` = 'foo'", 1212 BindVariables: map[string]*querypb.BindVariable{}, 1213 }} 1214 utils.MustMatch(t, wantQueries, sbc1.Queries) 1215 vars, err := sqltypes.BuildBindVariable([]any{sqltypes.NewVarChar("foo")}) 1216 require.NoError(t, err) 1217 wantQueries = []*querypb.BoundQuery{{ 1218 Sql: "select `name`, user_id from name_user_map where `name` in ::name", 1219 BindVariables: map[string]*querypb.BindVariable{ 1220 "name": vars, 1221 }, 1222 }} 1223 utils.MustMatch(t, wantQueries, sbclookup.Queries) 1224 } 1225 1226 func TestSelectINFromOR(t *testing.T) { 1227 executor, sbc1, _, _ := createExecutorEnv() 1228 executor.pv = querypb.ExecuteOptions_Gen4 1229 1230 _, err := executorExec(executor, "select 1 from user where id = 1 and name = 'apa' or id = 2 and name = 'toto'", nil) 1231 require.NoError(t, err) 1232 wantQueries := []*querypb.BoundQuery{{ 1233 Sql: "select 1 from `user` where id = 1 and `name` = 'apa' or id = 2 and `name` = 'toto'", 1234 BindVariables: map[string]*querypb.BindVariable{ 1235 "__vals": sqltypes.TestBindVariable([]any{int64(1), int64(2)}), 1236 }, 1237 }} 1238 utils.MustMatch(t, wantQueries, sbc1.Queries) 1239 } 1240 1241 func TestSelectDual(t *testing.T) { 1242 executor, sbc1, _, lookup := createExecutorEnv() 1243 1244 _, err := executorExec(executor, "select @@aa.bb from dual", nil) 1245 require.NoError(t, err) 1246 wantQueries := []*querypb.BoundQuery{{ 1247 Sql: "select @@`aa.bb` from dual", 1248 BindVariables: map[string]*querypb.BindVariable{}, 1249 }} 1250 utils.MustMatch(t, wantQueries, sbc1.Queries) 1251 1252 _, err = executorExec(executor, "select @@aa.bb from TestUnsharded.dual", nil) 1253 require.NoError(t, err) 1254 utils.MustMatch(t, wantQueries, lookup.Queries) 1255 } 1256 1257 func TestSelectComments(t *testing.T) { 1258 executor, sbc1, sbc2, _ := createExecutorEnv() 1259 1260 _, err := executorExec(executor, "/* leading */ select id from user where id = 1 /* trailing */", nil) 1261 require.NoError(t, err) 1262 wantQueries := []*querypb.BoundQuery{{ 1263 Sql: "/* leading */ select id from `user` where id = 1 /* trailing */", 1264 BindVariables: map[string]*querypb.BindVariable{}, 1265 }} 1266 utils.MustMatch(t, wantQueries, sbc1.Queries) 1267 if sbc2.Queries != nil { 1268 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 1269 } 1270 sbc1.Queries = nil 1271 } 1272 1273 func TestSelectNormalize(t *testing.T) { 1274 executor, sbc1, sbc2, _ := createExecutorEnv() 1275 executor.normalize = true 1276 1277 _, err := executorExec(executor, "/* leading */ select id from user where id = 1 /* trailing */", nil) 1278 require.NoError(t, err) 1279 wantQueries := []*querypb.BoundQuery{{ 1280 Sql: "/* leading */ select id from `user` where id = :id /* trailing */", 1281 BindVariables: map[string]*querypb.BindVariable{ 1282 "id": sqltypes.TestBindVariable(int64(1)), 1283 }, 1284 }} 1285 utils.MustMatch(t, wantQueries, sbc1.Queries) 1286 if sbc2.Queries != nil { 1287 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 1288 } 1289 sbc1.Queries = nil 1290 1291 // Force the query to go to the "wrong" shard and ensure that normalization still happens 1292 primarySession.TargetString = "TestExecutor/40-60" 1293 _, err = executorExec(executor, "/* leading */ select id from user where id = 1 /* trailing */", nil) 1294 require.NoError(t, err) 1295 wantQueries = []*querypb.BoundQuery{{ 1296 Sql: "/* leading */ select id from `user` where id = :id /* trailing */", 1297 BindVariables: map[string]*querypb.BindVariable{ 1298 "id": sqltypes.TestBindVariable(int64(1)), 1299 }, 1300 }} 1301 require.Empty(t, sbc1.Queries) 1302 utils.MustMatch(t, wantQueries, sbc2.Queries, "sbc2.Queries") 1303 sbc2.Queries = nil 1304 primarySession.TargetString = "" 1305 } 1306 1307 func TestSelectCaseSensitivity(t *testing.T) { 1308 executor, sbc1, sbc2, _ := createExecutorEnv() 1309 1310 _, err := executorExec(executor, "select Id from user where iD = 1", nil) 1311 require.NoError(t, err) 1312 wantQueries := []*querypb.BoundQuery{{ 1313 Sql: "select Id from `user` where iD = 1", 1314 BindVariables: map[string]*querypb.BindVariable{}, 1315 }} 1316 utils.MustMatch(t, wantQueries, sbc1.Queries) 1317 if sbc2.Queries != nil { 1318 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 1319 } 1320 sbc1.Queries = nil 1321 } 1322 1323 func TestStreamSelectEqual(t *testing.T) { 1324 executor, _, _, _ := createExecutorEnv() 1325 1326 sql := "select id from user where id = 1" 1327 result, err := executorStream(executor, sql) 1328 require.NoError(t, err) 1329 wantResult := sandboxconn.StreamRowResult 1330 if !result.Equal(wantResult) { 1331 t.Errorf("result: %+v, want %+v", result, wantResult) 1332 } 1333 } 1334 1335 func TestSelectKeyRange(t *testing.T) { 1336 executor, sbc1, sbc2, _ := createExecutorEnv() 1337 1338 _, err := executorExec(executor, "select krcol_unique, krcol from keyrange_table where krcol = 1", nil) 1339 require.NoError(t, err) 1340 wantQueries := []*querypb.BoundQuery{{ 1341 Sql: "select krcol_unique, krcol from keyrange_table where krcol = 1", 1342 BindVariables: map[string]*querypb.BindVariable{}, 1343 }} 1344 utils.MustMatch(t, wantQueries, sbc1.Queries) 1345 if sbc2.Queries != nil { 1346 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 1347 } 1348 sbc1.Queries = nil 1349 } 1350 1351 func TestSelectKeyRangeUnique(t *testing.T) { 1352 executor, sbc1, sbc2, _ := createExecutorEnv() 1353 1354 _, err := executorExec(executor, "select krcol_unique, krcol from keyrange_table where krcol_unique = 1", nil) 1355 require.NoError(t, err) 1356 wantQueries := []*querypb.BoundQuery{{ 1357 Sql: "select krcol_unique, krcol from keyrange_table where krcol_unique = 1", 1358 BindVariables: map[string]*querypb.BindVariable{}, 1359 }} 1360 utils.MustMatch(t, wantQueries, sbc1.Queries) 1361 if sbc2.Queries != nil { 1362 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 1363 } 1364 sbc1.Queries = nil 1365 } 1366 1367 func TestSelectIN(t *testing.T) { 1368 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 1369 1370 // Constant in IN clause is just a number, not a bind variable. 1371 _, err := executorExec(executor, "select id from user where id in (1)", nil) 1372 require.NoError(t, err) 1373 wantQueries := []*querypb.BoundQuery{{ 1374 Sql: "select id from `user` where id in ::__vals", 1375 BindVariables: map[string]*querypb.BindVariable{ 1376 "__vals": sqltypes.TestBindVariable([]any{int64(1)}), 1377 }, 1378 }} 1379 utils.MustMatch(t, wantQueries, sbc1.Queries) 1380 if sbc2.Queries != nil { 1381 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 1382 } 1383 1384 // Constants in IN clause are just numbers, not bind variables. 1385 // They result in two different queries on two shards. 1386 sbc1.Queries = nil 1387 sbc2.Queries = nil 1388 _, err = executorExec(executor, "select id from user where id in (1, 3)", nil) 1389 require.NoError(t, err) 1390 wantQueries = []*querypb.BoundQuery{{ 1391 Sql: "select id from `user` where id in ::__vals", 1392 BindVariables: map[string]*querypb.BindVariable{ 1393 "__vals": sqltypes.TestBindVariable([]any{int64(1)}), 1394 }, 1395 }} 1396 utils.MustMatch(t, wantQueries, sbc1.Queries) 1397 wantQueries = []*querypb.BoundQuery{{ 1398 Sql: "select id from `user` where id in ::__vals", 1399 BindVariables: map[string]*querypb.BindVariable{ 1400 "__vals": sqltypes.TestBindVariable([]any{int64(3)}), 1401 }, 1402 }} 1403 utils.MustMatch(t, wantQueries, sbc2.Queries) 1404 1405 // In is a bind variable list, that will end up on two shards. 1406 // This is using []any for the bind variable list. 1407 sbc1.Queries = nil 1408 sbc2.Queries = nil 1409 _, err = executorExec(executor, "select id from user where id in ::vals", map[string]*querypb.BindVariable{ 1410 "vals": sqltypes.TestBindVariable([]any{int64(1), int64(3)}), 1411 }) 1412 require.NoError(t, err) 1413 wantQueries = []*querypb.BoundQuery{{ 1414 Sql: "select id from `user` where id in ::__vals", 1415 BindVariables: map[string]*querypb.BindVariable{ 1416 "__vals": sqltypes.TestBindVariable([]any{int64(1)}), 1417 "vals": sqltypes.TestBindVariable([]any{int64(1), int64(3)}), 1418 }, 1419 }} 1420 utils.MustMatch(t, wantQueries, sbc1.Queries) 1421 wantQueries = []*querypb.BoundQuery{{ 1422 Sql: "select id from `user` where id in ::__vals", 1423 BindVariables: map[string]*querypb.BindVariable{ 1424 "__vals": sqltypes.TestBindVariable([]any{int64(3)}), 1425 "vals": sqltypes.TestBindVariable([]any{int64(1), int64(3)}), 1426 }, 1427 }} 1428 utils.MustMatch(t, wantQueries, sbc2.Queries) 1429 1430 // Convert a non-list bind variable. 1431 sbc1.Queries = nil 1432 sbc2.Queries = nil 1433 sbclookup.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( 1434 sqltypes.MakeTestFields("b|a", "varbinary|varbinary"), 1435 "foo|1", 1436 )}) 1437 _, err = executorExec(executor, "select id from user where name = 'foo'", nil) 1438 require.NoError(t, err) 1439 wantQueries = []*querypb.BoundQuery{{ 1440 Sql: "select id from `user` where `name` = 'foo'", 1441 BindVariables: map[string]*querypb.BindVariable{}, 1442 }} 1443 utils.MustMatch(t, wantQueries, sbc1.Queries) 1444 vars, err := sqltypes.BuildBindVariable([]any{sqltypes.NewVarChar("foo")}) 1445 require.NoError(t, err) 1446 wantQueries = []*querypb.BoundQuery{{ 1447 Sql: "select `name`, user_id from name_user_map where `name` in ::name", 1448 BindVariables: map[string]*querypb.BindVariable{ 1449 "name": vars, 1450 }, 1451 }} 1452 utils.MustMatch(t, wantQueries, sbclookup.Queries) 1453 } 1454 1455 func TestStreamSelectIN(t *testing.T) { 1456 executor, _, _, sbclookup := createExecutorEnv() 1457 1458 sql := "select id from user where id in (1)" 1459 result, err := executorStream(executor, sql) 1460 require.NoError(t, err) 1461 wantResult := sandboxconn.StreamRowResult 1462 if !result.Equal(wantResult) { 1463 t.Errorf("result: %+v, want %+v", result, wantResult) 1464 } 1465 1466 sql = "select id from user where id in (1, 3)" 1467 result, err = executorStream(executor, sql) 1468 require.NoError(t, err) 1469 wantResult = &sqltypes.Result{ 1470 Fields: sandboxconn.StreamRowResult.Fields, 1471 Rows: [][]sqltypes.Value{ 1472 sandboxconn.StreamRowResult.Rows[0], 1473 sandboxconn.StreamRowResult.Rows[0], 1474 }, 1475 RowsAffected: 0, 1476 } 1477 if !result.Equal(wantResult) { 1478 t.Errorf("result: %+v, want %+v", result, wantResult) 1479 } 1480 1481 sql = "select id from user where name = 'foo'" 1482 result, err = executorStream(executor, sql) 1483 require.NoError(t, err) 1484 wantResult = sandboxconn.StreamRowResult 1485 if !result.Equal(wantResult) { 1486 t.Errorf("result: %+v, want %+v", result, wantResult) 1487 } 1488 1489 vars, err := sqltypes.BuildBindVariable([]any{sqltypes.NewVarChar("foo")}) 1490 require.NoError(t, err) 1491 wantQueries := []*querypb.BoundQuery{{ 1492 Sql: "select `name`, user_id from name_user_map where `name` in ::name", 1493 BindVariables: map[string]*querypb.BindVariable{ 1494 "name": vars, 1495 }, 1496 }} 1497 utils.MustMatch(t, wantQueries, sbclookup.Queries) 1498 } 1499 1500 func createExecutor(serv *sandboxTopo, cell string, resolver *Resolver) *Executor { 1501 return NewExecutor(context.Background(), serv, cell, resolver, false, false, testBufferSize, cache.DefaultConfig, nil, false, querypb.ExecuteOptions_V3) 1502 } 1503 1504 func TestSelectScatter(t *testing.T) { 1505 // Special setup: Don't use createExecutorEnv. 1506 cell := "aa" 1507 hc := discovery.NewFakeHealthCheck(nil) 1508 s := createSandbox(KsTestSharded) 1509 s.VSchema = executorVSchema 1510 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1511 serv := newSandboxForCells([]string{cell}) 1512 resolver := newTestResolver(hc, serv, cell) 1513 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1514 var conns []*sandboxconn.SandboxConn 1515 for _, shard := range shards { 1516 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1517 conns = append(conns, sbc) 1518 } 1519 executor := createExecutor(serv, cell, resolver) 1520 logChan := QueryLogger.Subscribe("Test") 1521 defer QueryLogger.Unsubscribe(logChan) 1522 1523 sql := "select id from user" 1524 _, err := executorExec(executor, sql, nil) 1525 require.NoError(t, err) 1526 wantQueries := []*querypb.BoundQuery{{ 1527 Sql: "select id from `user`", 1528 BindVariables: map[string]*querypb.BindVariable{}, 1529 }} 1530 for _, conn := range conns { 1531 utils.MustMatch(t, wantQueries, conn.Queries) 1532 } 1533 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 8) 1534 } 1535 1536 func TestSelectScatterPartial(t *testing.T) { 1537 // Special setup: Don't use createExecutorEnv. 1538 primarySession = &vtgatepb.Session{ 1539 TargetString: "@primary", 1540 } 1541 cell := "aa" 1542 hc := discovery.NewFakeHealthCheck(nil) 1543 s := createSandbox(KsTestSharded) 1544 s.VSchema = executorVSchema 1545 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1546 serv := newSandboxForCells([]string{cell}) 1547 resolver := newTestResolver(hc, serv, cell) 1548 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1549 var conns []*sandboxconn.SandboxConn 1550 for _, shard := range shards { 1551 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1552 conns = append(conns, sbc) 1553 } 1554 1555 executor := createExecutor(serv, cell, resolver) 1556 logChan := QueryLogger.Subscribe("Test") 1557 defer QueryLogger.Unsubscribe(logChan) 1558 1559 // Fail 1 of N without the directive fails the whole operation 1560 conns[2].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1561 results, err := executorExec(executor, "select id from user", nil) 1562 wantErr := "TestExecutor.40-60.primary" 1563 if err == nil || !strings.Contains(err.Error(), wantErr) { 1564 t.Errorf("want error %v, got %v", wantErr, err) 1565 } 1566 if vterrors.Code(err) != vtrpcpb.Code_RESOURCE_EXHAUSTED { 1567 t.Errorf("want error code Code_RESOURCE_EXHAUSTED, but got %v", vterrors.Code(err)) 1568 } 1569 if results != nil { 1570 t.Errorf("want nil results, got %v", results) 1571 } 1572 testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user", 8) 1573 1574 // Fail 1 of N with the directive succeeds with 7 rows 1575 results, err = executorExec(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", nil) 1576 require.NoError(t, err) 1577 if results == nil || len(results.Rows) != 7 { 1578 t.Errorf("want 7 results, got %v", results) 1579 } 1580 testQueryLog(t, logChan, "TestExecute", "SELECT", "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", 8) 1581 1582 // When all shards fail, the execution should also fail 1583 conns[0].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1584 conns[1].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1585 conns[3].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1586 conns[4].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1587 conns[5].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1588 conns[6].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1589 conns[7].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1590 1591 _, err = executorExec(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", nil) 1592 require.Error(t, err) 1593 testQueryLog(t, logChan, "TestExecute", "SELECT", "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", 8) 1594 1595 _, err = executorExec(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user order by id", nil) 1596 require.Error(t, err) 1597 } 1598 1599 func TestSelectScatterPartialOLAP(t *testing.T) { 1600 // Special setup: Don't use createExecutorEnv. 1601 cell := "aa" 1602 hc := discovery.NewFakeHealthCheck(nil) 1603 s := createSandbox(KsTestSharded) 1604 s.VSchema = executorVSchema 1605 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1606 serv := newSandboxForCells([]string{cell}) 1607 resolver := newTestResolver(hc, serv, cell) 1608 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1609 var conns []*sandboxconn.SandboxConn 1610 for _, shard := range shards { 1611 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1612 conns = append(conns, sbc) 1613 } 1614 1615 executor := createExecutor(serv, cell, resolver) 1616 logChan := QueryLogger.Subscribe("Test") 1617 defer QueryLogger.Unsubscribe(logChan) 1618 1619 // Fail 1 of N without the directive fails the whole operation 1620 conns[2].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1621 results, err := executorStream(executor, "select id from user") 1622 assert.EqualError(t, err, "target: TestExecutor.40-60.primary: RESOURCE_EXHAUSTED error") 1623 assert.Equal(t, vtrpcpb.Code_RESOURCE_EXHAUSTED, vterrors.Code(err)) 1624 assert.Nil(t, results) 1625 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", "select id from user", 8) 1626 1627 // Fail 1 of N with the directive succeeds with 7 rows 1628 results, err = executorStream(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user") 1629 require.NoError(t, err) 1630 assert.EqualValues(t, 7, len(results.Rows)) 1631 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", 8) 1632 1633 // If all shards fail, the operation should also fail 1634 conns[0].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1635 conns[1].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1636 conns[3].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1637 conns[4].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1638 conns[5].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1639 conns[6].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1640 conns[7].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 1641 1642 _, err = executorStream(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user") 1643 require.Error(t, err) 1644 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", 8) 1645 1646 _, err = executorStream(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user order by id") 1647 require.Error(t, err) 1648 } 1649 1650 func TestSelectScatterPartialOLAP2(t *testing.T) { 1651 // Special setup: Don't use createExecutorEnv. 1652 cell := "aa" 1653 hc := discovery.NewFakeHealthCheck(nil) 1654 s := createSandbox(KsTestSharded) 1655 s.VSchema = executorVSchema 1656 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1657 serv := newSandboxForCells([]string{cell}) 1658 resolver := newTestResolver(hc, serv, cell) 1659 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1660 var conns []*sandboxconn.SandboxConn 1661 for _, shard := range shards { 1662 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1663 conns = append(conns, sbc) 1664 } 1665 1666 executor := createExecutor(serv, cell, resolver) 1667 logChan := QueryLogger.Subscribe("Test") 1668 defer QueryLogger.Unsubscribe(logChan) 1669 1670 // Fail 1 of N without the directive fails the whole operation 1671 tablet0 := conns[2].Tablet() 1672 ths := hc.GetHealthyTabletStats(&querypb.Target{ 1673 Keyspace: tablet0.GetKeyspace(), 1674 Shard: tablet0.GetShard(), 1675 TabletType: tablet0.GetType(), 1676 }) 1677 sbc0Th := ths[0] 1678 sbc0Th.Serving = false 1679 1680 results, err := executorStream(executor, "select id from user") 1681 require.Error(t, err) 1682 assert.Contains(t, err.Error(), `no healthy tablet available for 'keyspace:"TestExecutor" shard:"40-60"`) 1683 assert.Equal(t, vtrpcpb.Code_UNAVAILABLE, vterrors.Code(err)) 1684 assert.Nil(t, results) 1685 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", "select id from user", 8) 1686 1687 // Fail 1 of N with the directive succeeds with 7 rows 1688 results, err = executorStream(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user") 1689 require.NoError(t, err) 1690 assert.EqualValues(t, 7, len(results.Rows)) 1691 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", 8) 1692 1693 // order by 1694 results, err = executorStream(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user order by id") 1695 require.NoError(t, err) 1696 assert.EqualValues(t, 7, len(results.Rows)) 1697 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user order by id", 8) 1698 1699 // order by and limit 1700 results, err = executorStream(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user order by id limit 5") 1701 require.NoError(t, err) 1702 assert.EqualValues(t, 5, len(results.Rows)) 1703 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user order by id limit 5", 8) 1704 } 1705 1706 func TestStreamSelectScatter(t *testing.T) { 1707 // Special setup: Don't use createExecutorEnv. 1708 cell := "aa" 1709 hc := discovery.NewFakeHealthCheck(nil) 1710 s := createSandbox(KsTestSharded) 1711 s.VSchema = executorVSchema 1712 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1713 serv := newSandboxForCells([]string{cell}) 1714 resolver := newTestResolver(hc, serv, cell) 1715 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1716 for _, shard := range shards { 1717 _ = hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1718 } 1719 executor := createExecutor(serv, cell, resolver) 1720 1721 sql := "select id from user" 1722 result, err := executorStream(executor, sql) 1723 require.NoError(t, err) 1724 wantResult := &sqltypes.Result{ 1725 Fields: sandboxconn.SingleRowResult.Fields, 1726 Rows: [][]sqltypes.Value{ 1727 sandboxconn.StreamRowResult.Rows[0], 1728 sandboxconn.StreamRowResult.Rows[0], 1729 sandboxconn.StreamRowResult.Rows[0], 1730 sandboxconn.StreamRowResult.Rows[0], 1731 sandboxconn.StreamRowResult.Rows[0], 1732 sandboxconn.StreamRowResult.Rows[0], 1733 sandboxconn.StreamRowResult.Rows[0], 1734 sandboxconn.StreamRowResult.Rows[0], 1735 }, 1736 } 1737 utils.MustMatch(t, wantResult, result) 1738 } 1739 1740 // TestSelectScatterOrderBy will run an ORDER BY query that will scatter out to 8 shards and return the 8 rows (one per shard) sorted. 1741 func TestSelectScatterOrderBy(t *testing.T) { 1742 // Special setup: Don't use createExecutorEnv. 1743 cell := "aa" 1744 hc := discovery.NewFakeHealthCheck(nil) 1745 s := createSandbox(KsTestSharded) 1746 s.VSchema = executorVSchema 1747 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1748 serv := newSandboxForCells([]string{cell}) 1749 resolver := newTestResolver(hc, serv, cell) 1750 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1751 var conns []*sandboxconn.SandboxConn 1752 for i, shard := range shards { 1753 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1754 sbc.SetResults([]*sqltypes.Result{{ 1755 Fields: []*querypb.Field{ 1756 {Name: "col1", Type: sqltypes.Int32}, 1757 {Name: "col2", Type: sqltypes.Int32}, 1758 {Name: "weight_string(col2)", Type: sqltypes.VarBinary}, 1759 }, 1760 InsertID: 0, 1761 Rows: [][]sqltypes.Value{{ 1762 sqltypes.NewInt32(1), 1763 // i%4 ensures that there are duplicates across shards. 1764 // This will allow us to test that cross-shard ordering 1765 // still works correctly. 1766 sqltypes.NewInt32(int32(i % 4)), 1767 sqltypes.NULL, 1768 }}, 1769 }}) 1770 conns = append(conns, sbc) 1771 } 1772 executor := createExecutor(serv, cell, resolver) 1773 1774 query := "select col1, col2 from user order by col2 desc" 1775 gotResult, err := executorExec(executor, query, nil) 1776 require.NoError(t, err) 1777 1778 wantQueries := []*querypb.BoundQuery{{ 1779 Sql: "select col1, col2, weight_string(col2) from `user` order by col2 desc", 1780 BindVariables: map[string]*querypb.BindVariable{}, 1781 }} 1782 for _, conn := range conns { 1783 utils.MustMatch(t, wantQueries, conn.Queries) 1784 } 1785 1786 wantResult := &sqltypes.Result{ 1787 Fields: []*querypb.Field{ 1788 {Name: "col1", Type: sqltypes.Int32}, 1789 {Name: "col2", Type: sqltypes.Int32}, 1790 }, 1791 InsertID: 0, 1792 } 1793 for i := 0; i < 4; i++ { 1794 // There should be a duplicate for each row returned. 1795 for j := 0; j < 2; j++ { 1796 row := []sqltypes.Value{ 1797 sqltypes.NewInt32(1), 1798 sqltypes.NewInt32(int32(3 - i)), 1799 } 1800 wantResult.Rows = append(wantResult.Rows, row) 1801 } 1802 } 1803 utils.MustMatch(t, wantResult, gotResult) 1804 } 1805 1806 // TestSelectScatterOrderByVarChar will run an ORDER BY query that will scatter out to 8 shards and return the 8 rows (one per shard) sorted. 1807 func TestSelectScatterOrderByVarChar(t *testing.T) { 1808 // Special setup: Don't use createExecutorEnv. 1809 cell := "aa" 1810 hc := discovery.NewFakeHealthCheck(nil) 1811 s := createSandbox(KsTestSharded) 1812 s.VSchema = executorVSchema 1813 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1814 serv := newSandboxForCells([]string{cell}) 1815 resolver := newTestResolver(hc, serv, cell) 1816 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1817 var conns []*sandboxconn.SandboxConn 1818 for i, shard := range shards { 1819 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1820 sbc.SetResults([]*sqltypes.Result{{ 1821 Fields: []*querypb.Field{ 1822 {Name: "col1", Type: sqltypes.Int32}, 1823 {Name: "textcol", Type: sqltypes.VarChar}, 1824 }, 1825 InsertID: 0, 1826 Rows: [][]sqltypes.Value{{ 1827 sqltypes.NewInt32(1), 1828 // i%4 ensures that there are duplicates across shards. 1829 // This will allow us to test that cross-shard ordering 1830 // still works correctly. 1831 sqltypes.NewVarChar(fmt.Sprintf("%d", i%4)), 1832 sqltypes.NewVarBinary(fmt.Sprintf("%d", i%4)), 1833 }}, 1834 }}) 1835 conns = append(conns, sbc) 1836 } 1837 executor := createExecutor(serv, cell, resolver) 1838 1839 query := "select col1, textcol from user order by textcol desc" 1840 gotResult, err := executorExec(executor, query, nil) 1841 require.NoError(t, err) 1842 1843 wantQueries := []*querypb.BoundQuery{{ 1844 Sql: "select col1, textcol, weight_string(textcol) from `user` order by textcol desc", 1845 BindVariables: map[string]*querypb.BindVariable{}, 1846 }} 1847 for _, conn := range conns { 1848 utils.MustMatch(t, wantQueries, conn.Queries) 1849 } 1850 1851 wantResult := &sqltypes.Result{ 1852 Fields: []*querypb.Field{ 1853 {Name: "col1", Type: sqltypes.Int32}, 1854 {Name: "textcol", Type: sqltypes.VarChar}, 1855 }, 1856 InsertID: 0, 1857 } 1858 for i := 0; i < 4; i++ { 1859 // There should be a duplicate for each row returned. 1860 for j := 0; j < 2; j++ { 1861 row := []sqltypes.Value{ 1862 sqltypes.NewInt32(1), 1863 sqltypes.NewVarChar(fmt.Sprintf("%d", 3-i)), 1864 } 1865 wantResult.Rows = append(wantResult.Rows, row) 1866 } 1867 } 1868 utils.MustMatch(t, wantResult, gotResult) 1869 } 1870 1871 func TestStreamSelectScatterOrderBy(t *testing.T) { 1872 // Special setup: Don't use createExecutorEnv. 1873 cell := "aa" 1874 hc := discovery.NewFakeHealthCheck(nil) 1875 s := createSandbox(KsTestSharded) 1876 s.VSchema = executorVSchema 1877 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1878 serv := newSandboxForCells([]string{cell}) 1879 resolver := newTestResolver(hc, serv, cell) 1880 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1881 var conns []*sandboxconn.SandboxConn 1882 for i, shard := range shards { 1883 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1884 sbc.SetResults([]*sqltypes.Result{{ 1885 Fields: []*querypb.Field{ 1886 {Name: "id", Type: sqltypes.Int32}, 1887 {Name: "col", Type: sqltypes.Int32}, 1888 {Name: "weight_string(col)", Type: sqltypes.VarBinary}, 1889 }, 1890 InsertID: 0, 1891 Rows: [][]sqltypes.Value{{ 1892 sqltypes.NewInt32(1), 1893 sqltypes.NewInt32(int32(i % 4)), 1894 sqltypes.NULL, 1895 }}, 1896 }}) 1897 conns = append(conns, sbc) 1898 } 1899 executor := createExecutor(serv, cell, resolver) 1900 1901 query := "select id, col from user order by col desc" 1902 gotResult, err := executorStream(executor, query) 1903 require.NoError(t, err) 1904 1905 wantQueries := []*querypb.BoundQuery{{ 1906 Sql: "select id, col, weight_string(col) from `user` order by col desc", 1907 BindVariables: map[string]*querypb.BindVariable{}, 1908 }} 1909 for _, conn := range conns { 1910 utils.MustMatch(t, wantQueries, conn.Queries) 1911 } 1912 1913 wantResult := &sqltypes.Result{ 1914 Fields: []*querypb.Field{ 1915 {Name: "id", Type: sqltypes.Int32}, 1916 {Name: "col", Type: sqltypes.Int32}, 1917 }, 1918 } 1919 for i := 0; i < 4; i++ { 1920 row := []sqltypes.Value{ 1921 sqltypes.NewInt32(1), 1922 sqltypes.NewInt32(int32(3 - i)), 1923 } 1924 wantResult.Rows = append(wantResult.Rows, row, row) 1925 } 1926 utils.MustMatch(t, wantResult, gotResult) 1927 } 1928 1929 func TestStreamSelectScatterOrderByVarChar(t *testing.T) { 1930 // Special setup: Don't use createExecutorEnv. 1931 cell := "aa" 1932 hc := discovery.NewFakeHealthCheck(nil) 1933 s := createSandbox(KsTestSharded) 1934 s.VSchema = executorVSchema 1935 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1936 serv := newSandboxForCells([]string{cell}) 1937 resolver := newTestResolver(hc, serv, cell) 1938 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1939 var conns []*sandboxconn.SandboxConn 1940 for i, shard := range shards { 1941 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 1942 sbc.SetResults([]*sqltypes.Result{{ 1943 Fields: []*querypb.Field{ 1944 {Name: "id", Type: sqltypes.Int32}, 1945 {Name: "textcol", Type: sqltypes.VarChar}, 1946 }, 1947 InsertID: 0, 1948 Rows: [][]sqltypes.Value{{ 1949 sqltypes.NewInt32(1), 1950 sqltypes.NewVarChar(fmt.Sprintf("%d", i%4)), 1951 sqltypes.NewVarBinary(fmt.Sprintf("%d", i%4)), 1952 }}, 1953 }}) 1954 conns = append(conns, sbc) 1955 } 1956 executor := createExecutor(serv, cell, resolver) 1957 1958 query := "select id, textcol from user order by textcol desc" 1959 gotResult, err := executorStream(executor, query) 1960 require.NoError(t, err) 1961 1962 wantQueries := []*querypb.BoundQuery{{ 1963 Sql: "select id, textcol, weight_string(textcol) from `user` order by textcol desc", 1964 BindVariables: map[string]*querypb.BindVariable{}, 1965 }} 1966 for _, conn := range conns { 1967 utils.MustMatch(t, wantQueries, conn.Queries) 1968 } 1969 1970 wantResult := &sqltypes.Result{ 1971 Fields: []*querypb.Field{ 1972 {Name: "id", Type: sqltypes.Int32}, 1973 {Name: "textcol", Type: sqltypes.VarChar}, 1974 }, 1975 } 1976 for i := 0; i < 4; i++ { 1977 row := []sqltypes.Value{ 1978 sqltypes.NewInt32(1), 1979 sqltypes.NewVarChar(fmt.Sprintf("%d", 3-i)), 1980 } 1981 wantResult.Rows = append(wantResult.Rows, row, row) 1982 } 1983 utils.MustMatch(t, wantResult, gotResult) 1984 } 1985 1986 // TestSelectScatterAggregate will run an aggregate query that will scatter out to 8 shards and return 4 aggregated rows. 1987 func TestSelectScatterAggregate(t *testing.T) { 1988 // Special setup: Don't use createExecutorEnv. 1989 cell := "aa" 1990 hc := discovery.NewFakeHealthCheck(nil) 1991 s := createSandbox(KsTestSharded) 1992 s.VSchema = executorVSchema 1993 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 1994 serv := newSandboxForCells([]string{cell}) 1995 resolver := newTestResolver(hc, serv, cell) 1996 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 1997 var conns []*sandboxconn.SandboxConn 1998 for i, shard := range shards { 1999 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 2000 sbc.SetResults([]*sqltypes.Result{{ 2001 Fields: []*querypb.Field{ 2002 {Name: "col", Type: sqltypes.Int32}, 2003 {Name: "sum(foo)", Type: sqltypes.Int32}, 2004 {Name: "weight_string(col)", Type: sqltypes.VarBinary}, 2005 }, 2006 InsertID: 0, 2007 Rows: [][]sqltypes.Value{{ 2008 sqltypes.NewInt32(int32(i % 4)), 2009 sqltypes.NewInt32(int32(i)), 2010 sqltypes.NULL, 2011 }}, 2012 }}) 2013 conns = append(conns, sbc) 2014 } 2015 executor := createExecutor(serv, cell, resolver) 2016 2017 query := "select col, sum(foo) from user group by col" 2018 gotResult, err := executorExec(executor, query, nil) 2019 require.NoError(t, err) 2020 2021 wantQueries := []*querypb.BoundQuery{{ 2022 Sql: "select col, sum(foo), weight_string(col) from `user` group by col, weight_string(col) order by col asc", 2023 BindVariables: map[string]*querypb.BindVariable{}, 2024 }} 2025 for _, conn := range conns { 2026 utils.MustMatch(t, wantQueries, conn.Queries) 2027 } 2028 2029 wantResult := &sqltypes.Result{ 2030 Fields: []*querypb.Field{ 2031 {Name: "col", Type: sqltypes.Int32}, 2032 {Name: "sum(foo)", Type: sqltypes.Int32}, 2033 }, 2034 InsertID: 0, 2035 } 2036 for i := 0; i < 4; i++ { 2037 row := []sqltypes.Value{ 2038 sqltypes.NewInt32(int32(i)), 2039 sqltypes.NewInt32(int32(i*2 + 4)), 2040 } 2041 wantResult.Rows = append(wantResult.Rows, row) 2042 } 2043 utils.MustMatch(t, wantResult, gotResult) 2044 } 2045 2046 func TestStreamSelectScatterAggregate(t *testing.T) { 2047 // Special setup: Don't use createExecutorEnv. 2048 cell := "aa" 2049 hc := discovery.NewFakeHealthCheck(nil) 2050 s := createSandbox(KsTestSharded) 2051 s.VSchema = executorVSchema 2052 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 2053 serv := newSandboxForCells([]string{cell}) 2054 resolver := newTestResolver(hc, serv, cell) 2055 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 2056 var conns []*sandboxconn.SandboxConn 2057 for i, shard := range shards { 2058 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 2059 sbc.SetResults([]*sqltypes.Result{{ 2060 Fields: []*querypb.Field{ 2061 {Name: "col", Type: sqltypes.Int32}, 2062 {Name: "sum(foo)", Type: sqltypes.Int32}, 2063 {Name: "weight_string(col)", Type: sqltypes.VarBinary}, 2064 }, 2065 InsertID: 0, 2066 Rows: [][]sqltypes.Value{{ 2067 sqltypes.NewInt32(int32(i % 4)), 2068 sqltypes.NewInt32(int32(i)), 2069 sqltypes.NULL, 2070 }}, 2071 }}) 2072 conns = append(conns, sbc) 2073 } 2074 executor := createExecutor(serv, cell, resolver) 2075 2076 query := "select col, sum(foo) from user group by col" 2077 gotResult, err := executorStream(executor, query) 2078 require.NoError(t, err) 2079 2080 wantQueries := []*querypb.BoundQuery{{ 2081 Sql: "select col, sum(foo), weight_string(col) from `user` group by col, weight_string(col) order by col asc", 2082 BindVariables: map[string]*querypb.BindVariable{}, 2083 }} 2084 for _, conn := range conns { 2085 utils.MustMatch(t, wantQueries, conn.Queries) 2086 } 2087 2088 wantResult := &sqltypes.Result{ 2089 Fields: []*querypb.Field{ 2090 {Name: "col", Type: sqltypes.Int32}, 2091 {Name: "sum(foo)", Type: sqltypes.Int32}, 2092 }, 2093 } 2094 for i := 0; i < 4; i++ { 2095 row := []sqltypes.Value{ 2096 sqltypes.NewInt32(int32(i)), 2097 sqltypes.NewInt32(int32(i*2 + 4)), 2098 } 2099 wantResult.Rows = append(wantResult.Rows, row) 2100 } 2101 utils.MustMatch(t, wantResult, gotResult) 2102 } 2103 2104 // TestSelectScatterLimit will run a limit query (ordered for consistency) against 2105 // a scatter route and verify that the limit primitive works as intended. 2106 func TestSelectScatterLimit(t *testing.T) { 2107 // Special setup: Don't use createExecutorEnv. 2108 cell := "aa" 2109 hc := discovery.NewFakeHealthCheck(nil) 2110 s := createSandbox(KsTestSharded) 2111 s.VSchema = executorVSchema 2112 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 2113 serv := newSandboxForCells([]string{cell}) 2114 resolver := newTestResolver(hc, serv, cell) 2115 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 2116 var conns []*sandboxconn.SandboxConn 2117 for i, shard := range shards { 2118 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 2119 sbc.SetResults([]*sqltypes.Result{{ 2120 Fields: []*querypb.Field{ 2121 {Name: "col1", Type: sqltypes.Int32}, 2122 {Name: "col2", Type: sqltypes.Int32}, 2123 {Name: "weight_string(col2)", Type: sqltypes.VarBinary}, 2124 }, 2125 InsertID: 0, 2126 Rows: [][]sqltypes.Value{{ 2127 sqltypes.NewInt32(1), 2128 sqltypes.NewInt32(int32(i % 4)), 2129 sqltypes.NULL, 2130 }}, 2131 }}) 2132 conns = append(conns, sbc) 2133 } 2134 executor := createExecutor(serv, cell, resolver) 2135 2136 query := "select col1, col2 from user order by col2 desc limit 3" 2137 gotResult, err := executorExec(executor, query, nil) 2138 require.NoError(t, err) 2139 2140 wantQueries := []*querypb.BoundQuery{{ 2141 Sql: "select col1, col2, weight_string(col2) from `user` order by col2 desc limit :__upper_limit", 2142 BindVariables: map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)}, 2143 }} 2144 for _, conn := range conns { 2145 utils.MustMatch(t, wantQueries, conn.Queries) 2146 } 2147 2148 wantResult := &sqltypes.Result{ 2149 Fields: []*querypb.Field{ 2150 {Name: "col1", Type: sqltypes.Int32}, 2151 {Name: "col2", Type: sqltypes.Int32}, 2152 }, 2153 InsertID: 0, 2154 } 2155 wantResult.Rows = append(wantResult.Rows, 2156 []sqltypes.Value{ 2157 sqltypes.NewInt32(1), 2158 sqltypes.NewInt32(3), 2159 }, 2160 []sqltypes.Value{ 2161 sqltypes.NewInt32(1), 2162 sqltypes.NewInt32(3), 2163 }, 2164 []sqltypes.Value{ 2165 sqltypes.NewInt32(1), 2166 sqltypes.NewInt32(2), 2167 }) 2168 2169 utils.MustMatch(t, wantResult, gotResult) 2170 } 2171 2172 // TestStreamSelectScatterLimit will run a streaming limit query (ordered for consistency) against 2173 // a scatter route and verify that the limit primitive works as intended. 2174 func TestStreamSelectScatterLimit(t *testing.T) { 2175 // Special setup: Don't use createExecutorEnv. 2176 cell := "aa" 2177 hc := discovery.NewFakeHealthCheck(nil) 2178 s := createSandbox(KsTestSharded) 2179 s.VSchema = executorVSchema 2180 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 2181 serv := newSandboxForCells([]string{cell}) 2182 resolver := newTestResolver(hc, serv, cell) 2183 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 2184 var conns []*sandboxconn.SandboxConn 2185 for i, shard := range shards { 2186 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 2187 sbc.SetResults([]*sqltypes.Result{{ 2188 Fields: []*querypb.Field{ 2189 {Name: "col1", Type: sqltypes.Int32}, 2190 {Name: "col2", Type: sqltypes.Int32}, 2191 {Name: "weight_string(col2)", Type: sqltypes.VarBinary}, 2192 }, 2193 InsertID: 0, 2194 Rows: [][]sqltypes.Value{{ 2195 sqltypes.NewInt32(1), 2196 sqltypes.NewInt32(int32(i % 4)), 2197 sqltypes.NULL, 2198 }}, 2199 }}) 2200 conns = append(conns, sbc) 2201 } 2202 executor := createExecutor(serv, cell, resolver) 2203 2204 query := "select col1, col2 from user order by col2 desc limit 3" 2205 gotResult, err := executorStream(executor, query) 2206 require.NoError(t, err) 2207 2208 wantQueries := []*querypb.BoundQuery{{ 2209 Sql: "select col1, col2, weight_string(col2) from `user` order by col2 desc limit :__upper_limit", 2210 BindVariables: map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)}, 2211 }} 2212 for _, conn := range conns { 2213 utils.MustMatch(t, wantQueries, conn.Queries) 2214 } 2215 2216 wantResult := &sqltypes.Result{ 2217 Fields: []*querypb.Field{ 2218 {Name: "col1", Type: sqltypes.Int32}, 2219 {Name: "col2", Type: sqltypes.Int32}, 2220 }, 2221 } 2222 wantResult.Rows = append(wantResult.Rows, 2223 []sqltypes.Value{ 2224 sqltypes.NewInt32(1), 2225 sqltypes.NewInt32(3), 2226 }, 2227 []sqltypes.Value{ 2228 sqltypes.NewInt32(1), 2229 sqltypes.NewInt32(3), 2230 }, 2231 []sqltypes.Value{ 2232 sqltypes.NewInt32(1), 2233 sqltypes.NewInt32(2), 2234 }) 2235 2236 utils.MustMatch(t, wantResult, gotResult) 2237 } 2238 2239 // TODO(sougou): stream and non-stream testing are very similar. 2240 // Could reuse code, 2241 func TestSimpleJoin(t *testing.T) { 2242 executor, sbc1, sbc2, _ := createExecutorEnv() 2243 logChan := QueryLogger.Subscribe("Test") 2244 defer QueryLogger.Unsubscribe(logChan) 2245 2246 sql := "select u1.id, u2.id from user u1 join user u2 where u1.id = 1 and u2.id = 3" 2247 result, err := executorExec(executor, sql, nil) 2248 require.NoError(t, err) 2249 wantQueries := []*querypb.BoundQuery{{ 2250 Sql: "select u1.id from `user` as u1 where u1.id = 1", 2251 BindVariables: map[string]*querypb.BindVariable{}, 2252 }} 2253 utils.MustMatch(t, wantQueries, sbc1.Queries) 2254 wantQueries = []*querypb.BoundQuery{{ 2255 Sql: "select u2.id from `user` as u2 where u2.id = 3", 2256 BindVariables: map[string]*querypb.BindVariable{}, 2257 }} 2258 utils.MustMatch(t, wantQueries, sbc2.Queries) 2259 wantResult := &sqltypes.Result{ 2260 Fields: []*querypb.Field{ 2261 sandboxconn.SingleRowResult.Fields[0], 2262 sandboxconn.SingleRowResult.Fields[0], 2263 }, 2264 Rows: [][]sqltypes.Value{ 2265 { 2266 sandboxconn.SingleRowResult.Rows[0][0], 2267 sandboxconn.SingleRowResult.Rows[0][0], 2268 }, 2269 }, 2270 } 2271 if !result.Equal(wantResult) { 2272 t.Errorf("result: %+v, want %+v", result, wantResult) 2273 } 2274 2275 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 2) 2276 } 2277 2278 func TestJoinComments(t *testing.T) { 2279 executor, sbc1, sbc2, _ := createExecutorEnv() 2280 logChan := QueryLogger.Subscribe("Test") 2281 defer QueryLogger.Unsubscribe(logChan) 2282 2283 sql := "select u1.id, u2.id from user u1 join user u2 where u1.id = 1 and u2.id = 3 /* trailing */" 2284 _, err := executorExec(executor, sql, nil) 2285 require.NoError(t, err) 2286 wantQueries := []*querypb.BoundQuery{{ 2287 Sql: "select u1.id from `user` as u1 where u1.id = 1 /* trailing */", 2288 BindVariables: map[string]*querypb.BindVariable{}, 2289 }} 2290 utils.MustMatch(t, wantQueries, sbc1.Queries) 2291 wantQueries = []*querypb.BoundQuery{{ 2292 Sql: "select u2.id from `user` as u2 where u2.id = 3 /* trailing */", 2293 BindVariables: map[string]*querypb.BindVariable{}, 2294 }} 2295 utils.MustMatch(t, wantQueries, sbc2.Queries) 2296 2297 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 2) 2298 } 2299 2300 func TestSimpleJoinStream(t *testing.T) { 2301 executor, sbc1, sbc2, _ := createExecutorEnv() 2302 logChan := QueryLogger.Subscribe("Test") 2303 defer QueryLogger.Unsubscribe(logChan) 2304 2305 sql := "select u1.id, u2.id from user u1 join user u2 where u1.id = 1 and u2.id = 3" 2306 result, err := executorStream(executor, sql) 2307 require.NoError(t, err) 2308 wantQueries := []*querypb.BoundQuery{{ 2309 Sql: "select u1.id from `user` as u1 where u1.id = 1", 2310 BindVariables: map[string]*querypb.BindVariable{}, 2311 }} 2312 utils.MustMatch(t, wantQueries, sbc1.Queries) 2313 wantQueries = []*querypb.BoundQuery{{ 2314 Sql: "select u2.id from `user` as u2 where u2.id = 3", 2315 BindVariables: map[string]*querypb.BindVariable{}, 2316 }} 2317 utils.MustMatch(t, wantQueries, sbc2.Queries) 2318 wantResult := &sqltypes.Result{ 2319 Fields: []*querypb.Field{ 2320 sandboxconn.SingleRowResult.Fields[0], 2321 sandboxconn.SingleRowResult.Fields[0], 2322 }, 2323 Rows: [][]sqltypes.Value{ 2324 { 2325 sandboxconn.SingleRowResult.Rows[0][0], 2326 sandboxconn.SingleRowResult.Rows[0][0], 2327 }, 2328 }, 2329 RowsAffected: 0, 2330 } 2331 if !result.Equal(wantResult) { 2332 t.Errorf("result: %+v, want %+v", result, wantResult) 2333 } 2334 2335 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", sql, 2) 2336 } 2337 2338 func TestVarJoin(t *testing.T) { 2339 executor, sbc1, sbc2, _ := createExecutorEnv() 2340 logChan := QueryLogger.Subscribe("Test") 2341 defer QueryLogger.Unsubscribe(logChan) 2342 2343 result1 := []*sqltypes.Result{{ 2344 Fields: []*querypb.Field{ 2345 {Name: "id", Type: sqltypes.Int32}, 2346 {Name: "col", Type: sqltypes.Int32}, 2347 }, 2348 InsertID: 0, 2349 Rows: [][]sqltypes.Value{{ 2350 sqltypes.NewInt32(1), 2351 sqltypes.NewInt32(3), 2352 }}, 2353 }} 2354 sbc1.SetResults(result1) 2355 sql := "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1" 2356 _, err := executorExec(executor, sql, nil) 2357 require.NoError(t, err) 2358 wantQueries := []*querypb.BoundQuery{{ 2359 Sql: "select u1.id, u1.col from `user` as u1 where u1.id = 1", 2360 BindVariables: map[string]*querypb.BindVariable{}, 2361 }} 2362 utils.MustMatch(t, wantQueries, sbc1.Queries) 2363 // We have to use string representation because bindvars type is too complex. 2364 got := fmt.Sprintf("%+v", sbc2.Queries) 2365 want := `[sql:"select u2.id from ` + "`user`" + ` as u2 where u2.id = :u1_col" bind_variables:{key:"u1_col" value:{type:INT32 value:"3"}}]` 2366 if got != want { 2367 t.Errorf("sbc2.Queries: %s, want %s\n", got, want) 2368 } 2369 2370 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 2) 2371 } 2372 2373 func TestVarJoinStream(t *testing.T) { 2374 executor, sbc1, sbc2, _ := createExecutorEnv() 2375 logChan := QueryLogger.Subscribe("Test") 2376 defer QueryLogger.Unsubscribe(logChan) 2377 2378 result1 := []*sqltypes.Result{{ 2379 Fields: []*querypb.Field{ 2380 {Name: "id", Type: sqltypes.Int32}, 2381 {Name: "col", Type: sqltypes.Int32}, 2382 }, 2383 InsertID: 0, 2384 Rows: [][]sqltypes.Value{{ 2385 sqltypes.NewInt32(1), 2386 sqltypes.NewInt32(3), 2387 }}, 2388 }} 2389 sbc1.SetResults(result1) 2390 sql := "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1" 2391 _, err := executorStream(executor, sql) 2392 require.NoError(t, err) 2393 wantQueries := []*querypb.BoundQuery{{ 2394 Sql: "select u1.id, u1.col from `user` as u1 where u1.id = 1", 2395 BindVariables: map[string]*querypb.BindVariable{}, 2396 }} 2397 utils.MustMatch(t, wantQueries, sbc1.Queries) 2398 // We have to use string representation because bindvars type is too complex. 2399 got := fmt.Sprintf("%+v", sbc2.Queries) 2400 want := `[sql:"select u2.id from ` + "`user`" + ` as u2 where u2.id = :u1_col" bind_variables:{key:"u1_col" value:{type:INT32 value:"3"}}]` 2401 if got != want { 2402 t.Errorf("sbc2.Queries: %s, want %s\n", got, want) 2403 } 2404 2405 testQueryLog(t, logChan, "TestExecuteStream", "SELECT", sql, 2) 2406 } 2407 2408 func TestLeftJoin(t *testing.T) { 2409 executor, sbc1, sbc2, _ := createExecutorEnv() 2410 logChan := QueryLogger.Subscribe("Test") 2411 defer QueryLogger.Unsubscribe(logChan) 2412 result1 := []*sqltypes.Result{{ 2413 Fields: []*querypb.Field{ 2414 {Name: "col", Type: sqltypes.Int32}, 2415 {Name: "id", Type: sqltypes.Int32}, 2416 }, 2417 InsertID: 0, 2418 Rows: [][]sqltypes.Value{{ 2419 sqltypes.NewInt32(3), 2420 sqltypes.NewInt32(1), 2421 }}, 2422 }} 2423 emptyResult := []*sqltypes.Result{{ 2424 Fields: []*querypb.Field{ 2425 {Name: "id", Type: sqltypes.Int32}, 2426 }, 2427 }} 2428 sbc1.SetResults(result1) 2429 sbc2.SetResults(emptyResult) 2430 sql := "select u1.id, u2.id from user u1 left join user u2 on u2.id = u1.col where u1.id = 1" 2431 result, err := executorExec(executor, sql, nil) 2432 require.NoError(t, err) 2433 wantResult := &sqltypes.Result{ 2434 Fields: []*querypb.Field{ 2435 sandboxconn.SingleRowResult.Fields[0], 2436 sandboxconn.SingleRowResult.Fields[0], 2437 }, 2438 Rows: [][]sqltypes.Value{ 2439 { 2440 sandboxconn.SingleRowResult.Rows[0][0], 2441 {}, 2442 }, 2443 }, 2444 } 2445 if !result.Equal(wantResult) { 2446 t.Errorf("result: \n%+v, want \n%+v", result, wantResult) 2447 } 2448 testQueryLog(t, logChan, "TestExecute", "SELECT", sql, 2) 2449 } 2450 2451 func TestLeftJoinStream(t *testing.T) { 2452 executor, sbc1, sbc2, _ := createExecutorEnv() 2453 result1 := []*sqltypes.Result{{ 2454 Fields: []*querypb.Field{ 2455 {Name: "col", Type: sqltypes.Int32}, 2456 {Name: "id", Type: sqltypes.Int32}, 2457 }, 2458 InsertID: 0, 2459 Rows: [][]sqltypes.Value{{ 2460 sqltypes.NewInt32(3), 2461 sqltypes.NewInt32(1), 2462 }}, 2463 }} 2464 emptyResult := []*sqltypes.Result{{ 2465 Fields: []*querypb.Field{ 2466 {Name: "id", Type: sqltypes.Int32}, 2467 }, 2468 }} 2469 sbc1.SetResults(result1) 2470 sbc2.SetResults(emptyResult) 2471 result, err := executorStream(executor, "select u1.id, u2.id from user u1 left join user u2 on u2.id = u1.col where u1.id = 1") 2472 require.NoError(t, err) 2473 wantResult := &sqltypes.Result{ 2474 Fields: []*querypb.Field{ 2475 sandboxconn.SingleRowResult.Fields[0], 2476 sandboxconn.SingleRowResult.Fields[0], 2477 }, 2478 Rows: [][]sqltypes.Value{ 2479 { 2480 sandboxconn.SingleRowResult.Rows[0][0], 2481 {}, 2482 }, 2483 }, 2484 RowsAffected: 0, 2485 } 2486 if !result.Equal(wantResult) { 2487 t.Errorf("result: %+v, want %+v", result, wantResult) 2488 } 2489 } 2490 2491 func TestEmptyJoin(t *testing.T) { 2492 executor, sbc1, _, _ := createExecutorEnv() 2493 // Empty result requires a field query for the second part of join, 2494 // which is sent to shard 0. 2495 sbc1.SetResults([]*sqltypes.Result{{ 2496 Fields: []*querypb.Field{ 2497 {Name: "id", Type: sqltypes.Int32}, 2498 }, 2499 }, { 2500 Fields: []*querypb.Field{ 2501 {Name: "id", Type: sqltypes.Int32}, 2502 }, 2503 }}) 2504 result, err := executorExec(executor, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1", nil) 2505 require.NoError(t, err) 2506 wantQueries := []*querypb.BoundQuery{{ 2507 Sql: "select u1.id, u1.col from `user` as u1 where u1.id = 1", 2508 BindVariables: map[string]*querypb.BindVariable{}, 2509 }, { 2510 Sql: "select u2.id from `user` as u2 where 1 != 1", 2511 BindVariables: map[string]*querypb.BindVariable{ 2512 "u1_col": sqltypes.NullBindVariable, 2513 }, 2514 }} 2515 utils.MustMatch(t, wantQueries, sbc1.Queries) 2516 wantResult := &sqltypes.Result{ 2517 Fields: []*querypb.Field{ 2518 {Name: "id", Type: sqltypes.Int32}, 2519 {Name: "id", Type: sqltypes.Int32}, 2520 }, 2521 } 2522 if !result.Equal(wantResult) { 2523 t.Errorf("result: %+v, want %+v", result, wantResult) 2524 } 2525 } 2526 2527 func TestEmptyJoinStream(t *testing.T) { 2528 executor, sbc1, _, _ := createExecutorEnv() 2529 // Empty result requires a field query for the second part of join, 2530 // which is sent to shard 0. 2531 sbc1.SetResults([]*sqltypes.Result{{ 2532 Fields: []*querypb.Field{ 2533 {Name: "id", Type: sqltypes.Int32}, 2534 }, 2535 }, { 2536 Fields: []*querypb.Field{ 2537 {Name: "id", Type: sqltypes.Int32}, 2538 }, 2539 }}) 2540 result, err := executorStream(executor, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1") 2541 require.NoError(t, err) 2542 wantQueries := []*querypb.BoundQuery{{ 2543 Sql: "select u1.id, u1.col from `user` as u1 where u1.id = 1", 2544 BindVariables: map[string]*querypb.BindVariable{}, 2545 }, { 2546 Sql: "select u2.id from `user` as u2 where 1 != 1", 2547 BindVariables: map[string]*querypb.BindVariable{ 2548 "u1_col": sqltypes.NullBindVariable, 2549 }, 2550 }} 2551 utils.MustMatch(t, wantQueries, sbc1.Queries) 2552 wantResult := &sqltypes.Result{ 2553 Fields: []*querypb.Field{ 2554 {Name: "id", Type: sqltypes.Int32}, 2555 {Name: "id", Type: sqltypes.Int32}, 2556 }, 2557 } 2558 if !result.Equal(wantResult) { 2559 t.Errorf("result: %+v, want %+v", result, wantResult) 2560 } 2561 } 2562 2563 func TestEmptyJoinRecursive(t *testing.T) { 2564 executor, sbc1, _, _ := createExecutorEnv() 2565 // Make sure it also works recursively. 2566 sbc1.SetResults([]*sqltypes.Result{{ 2567 Fields: []*querypb.Field{ 2568 {Name: "id", Type: sqltypes.Int32}, 2569 }, 2570 }, { 2571 Fields: []*querypb.Field{ 2572 {Name: "id", Type: sqltypes.Int32}, 2573 {Name: "col", Type: sqltypes.Int32}, 2574 }, 2575 }, { 2576 Fields: []*querypb.Field{ 2577 {Name: "id", Type: sqltypes.Int32}, 2578 }, 2579 }}) 2580 result, err := executorExec(executor, "select u1.id, u2.id, u3.id from user u1 join (user u2 join user u3 on u3.id = u2.col) where u1.id = 1", nil) 2581 require.NoError(t, err) 2582 wantQueries := []*querypb.BoundQuery{{ 2583 Sql: "select u1.id from `user` as u1 where u1.id = 1", 2584 BindVariables: map[string]*querypb.BindVariable{}, 2585 }, { 2586 Sql: "select u2.id, u2.col from `user` as u2 where 1 != 1", 2587 BindVariables: map[string]*querypb.BindVariable{}, 2588 }, { 2589 Sql: "select u3.id from `user` as u3 where 1 != 1", 2590 BindVariables: map[string]*querypb.BindVariable{ 2591 "u2_col": sqltypes.NullBindVariable, 2592 }, 2593 }} 2594 utils.MustMatch(t, wantQueries, sbc1.Queries) 2595 wantResult := &sqltypes.Result{ 2596 Fields: []*querypb.Field{ 2597 {Name: "id", Type: sqltypes.Int32}, 2598 {Name: "id", Type: sqltypes.Int32}, 2599 {Name: "id", Type: sqltypes.Int32}, 2600 }, 2601 } 2602 if !result.Equal(wantResult) { 2603 t.Errorf("result: %+v, want %+v", result, wantResult) 2604 } 2605 } 2606 2607 func TestEmptyJoinRecursiveStream(t *testing.T) { 2608 executor, sbc1, _, _ := createExecutorEnv() 2609 // Make sure it also works recursively. 2610 sbc1.SetResults([]*sqltypes.Result{{ 2611 Fields: []*querypb.Field{ 2612 {Name: "id", Type: sqltypes.Int32}, 2613 }, 2614 }, { 2615 Fields: []*querypb.Field{ 2616 {Name: "id", Type: sqltypes.Int32}, 2617 {Name: "col", Type: sqltypes.Int32}, 2618 }, 2619 }, { 2620 Fields: []*querypb.Field{ 2621 {Name: "id", Type: sqltypes.Int32}, 2622 }, 2623 }}) 2624 result, err := executorStream(executor, "select u1.id, u2.id, u3.id from user u1 join (user u2 join user u3 on u3.id = u2.col) where u1.id = 1") 2625 require.NoError(t, err) 2626 wantQueries := []*querypb.BoundQuery{{ 2627 Sql: "select u1.id from `user` as u1 where u1.id = 1", 2628 BindVariables: map[string]*querypb.BindVariable{}, 2629 }, { 2630 Sql: "select u2.id, u2.col from `user` as u2 where 1 != 1", 2631 BindVariables: map[string]*querypb.BindVariable{}, 2632 }, { 2633 Sql: "select u3.id from `user` as u3 where 1 != 1", 2634 BindVariables: map[string]*querypb.BindVariable{ 2635 "u2_col": sqltypes.NullBindVariable, 2636 }, 2637 }} 2638 utils.MustMatch(t, wantQueries, sbc1.Queries) 2639 wantResult := &sqltypes.Result{ 2640 Fields: []*querypb.Field{ 2641 {Name: "id", Type: sqltypes.Int32}, 2642 {Name: "id", Type: sqltypes.Int32}, 2643 {Name: "id", Type: sqltypes.Int32}, 2644 }, 2645 } 2646 if !result.Equal(wantResult) { 2647 t.Errorf("result: %+v, want %+v", result, wantResult) 2648 } 2649 } 2650 2651 func TestCrossShardSubquery(t *testing.T) { 2652 executor, sbc1, sbc2, _ := createExecutorEnv() 2653 result1 := []*sqltypes.Result{{ 2654 Fields: []*querypb.Field{ 2655 {Name: "id", Type: sqltypes.Int32}, 2656 {Name: "col", Type: sqltypes.Int32}, 2657 }, 2658 InsertID: 0, 2659 Rows: [][]sqltypes.Value{{ 2660 sqltypes.NewInt32(1), 2661 sqltypes.NewInt32(3), 2662 }}, 2663 }} 2664 sbc1.SetResults(result1) 2665 result, err := executorExec(executor, "select id1 from (select u1.id id1, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1) as t", nil) 2666 require.NoError(t, err) 2667 wantQueries := []*querypb.BoundQuery{{ 2668 Sql: "select u1.id as id1, u1.col from `user` as u1 where u1.id = 1", 2669 BindVariables: map[string]*querypb.BindVariable{}, 2670 }} 2671 utils.MustMatch(t, wantQueries, sbc1.Queries) 2672 // We have to use string representation because bindvars type is too complex. 2673 got := fmt.Sprintf("%+v", sbc2.Queries) 2674 want := `[sql:"select u2.id from ` + "`user`" + ` as u2 where u2.id = :u1_col" bind_variables:{key:"u1_col" value:{type:INT32 value:"3"}}]` 2675 if got != want { 2676 t.Errorf("sbc2.Queries: %s, want %s\n", got, want) 2677 } 2678 2679 wantResult := &sqltypes.Result{ 2680 Fields: []*querypb.Field{ 2681 {Name: "id", Type: sqltypes.Int32}, 2682 }, 2683 Rows: [][]sqltypes.Value{{ 2684 sqltypes.NewInt32(1), 2685 }}, 2686 } 2687 if !result.Equal(wantResult) { 2688 t.Errorf("result: %+v, want %+v", result, wantResult) 2689 } 2690 } 2691 2692 func TestSubQueryAndQueryWithLimit(t *testing.T) { 2693 executor, sbc1, sbc2, _ := createExecutorEnv() 2694 result1 := []*sqltypes.Result{{ 2695 Fields: []*querypb.Field{ 2696 {Name: "id", Type: sqltypes.Int32}, 2697 {Name: "col", Type: sqltypes.Int32}, 2698 }, 2699 InsertID: 0, 2700 Rows: [][]sqltypes.Value{{ 2701 sqltypes.NewInt32(1), 2702 sqltypes.NewInt32(3), 2703 }}, 2704 }} 2705 result2 := []*sqltypes.Result{{ 2706 Fields: []*querypb.Field{ 2707 {Name: "id", Type: sqltypes.Int32}, 2708 {Name: "col", Type: sqltypes.Int32}, 2709 }, 2710 InsertID: 0, 2711 Rows: [][]sqltypes.Value{{ 2712 sqltypes.NewInt32(111), 2713 sqltypes.NewInt32(333), 2714 }}, 2715 }} 2716 sbc1.SetResults(result1) 2717 sbc2.SetResults(result2) 2718 2719 exec(executor, NewSafeSession(&vtgatepb.Session{ 2720 TargetString: "@primary", 2721 }), "select id1, id2 from t1 where id1 >= ( select id1 from t1 order by id1 asc limit 1) limit 100") 2722 require.Equal(t, 2, len(sbc1.Queries)) 2723 require.Equal(t, 2, len(sbc2.Queries)) 2724 2725 // sub query is evaluated first, and sees a limit of 1 2726 assert.Equal(t, `type:INT64 value:"1"`, sbc1.Queries[0].BindVariables["__upper_limit"].String()) 2727 assert.Equal(t, `type:INT64 value:"1"`, sbc2.Queries[0].BindVariables["__upper_limit"].String()) 2728 2729 // outer limit is only applied to the outer query 2730 assert.Equal(t, `type:INT64 value:"100"`, sbc1.Queries[1].BindVariables["__upper_limit"].String()) 2731 assert.Equal(t, `type:INT64 value:"100"`, sbc2.Queries[1].BindVariables["__upper_limit"].String()) 2732 } 2733 2734 func TestCrossShardSubqueryStream(t *testing.T) { 2735 executor, sbc1, sbc2, _ := createExecutorEnv() 2736 result1 := []*sqltypes.Result{{ 2737 Fields: []*querypb.Field{ 2738 {Name: "id", Type: sqltypes.Int32}, 2739 {Name: "col", Type: sqltypes.Int32}, 2740 }, 2741 InsertID: 0, 2742 Rows: [][]sqltypes.Value{{ 2743 sqltypes.NewInt32(1), 2744 sqltypes.NewInt32(3), 2745 }}, 2746 }} 2747 sbc1.SetResults(result1) 2748 result, err := executorStream(executor, "select id1 from (select u1.id id1, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1) as t") 2749 require.NoError(t, err) 2750 wantQueries := []*querypb.BoundQuery{{ 2751 Sql: "select u1.id as id1, u1.col from `user` as u1 where u1.id = 1", 2752 BindVariables: map[string]*querypb.BindVariable{}, 2753 }} 2754 utils.MustMatch(t, wantQueries, sbc1.Queries) 2755 // We have to use string representation because bindvars type is too complex. 2756 got := fmt.Sprintf("%+v", sbc2.Queries) 2757 want := `[sql:"select u2.id from ` + "`user`" + ` as u2 where u2.id = :u1_col" bind_variables:{key:"u1_col" value:{type:INT32 value:"3"}}]` 2758 if got != want { 2759 t.Errorf("sbc2.Queries:\n%s, want\n%s\n", got, want) 2760 } 2761 2762 wantResult := &sqltypes.Result{ 2763 Fields: []*querypb.Field{ 2764 {Name: "id", Type: sqltypes.Int32}, 2765 }, 2766 Rows: [][]sqltypes.Value{{ 2767 sqltypes.NewInt32(1), 2768 }}, 2769 } 2770 if !result.Equal(wantResult) { 2771 t.Errorf("result: %+v, want %+v", result, wantResult) 2772 } 2773 } 2774 2775 func TestCrossShardSubqueryGetFields(t *testing.T) { 2776 executor, sbc1, _, sbclookup := createExecutorEnv() 2777 sbclookup.SetResults([]*sqltypes.Result{{ 2778 Fields: []*querypb.Field{ 2779 {Name: "col", Type: sqltypes.Int32}, 2780 }, 2781 }}) 2782 result1 := []*sqltypes.Result{{ 2783 Fields: []*querypb.Field{ 2784 {Name: "id", Type: sqltypes.Int32}, 2785 {Name: "col", Type: sqltypes.Int32}, 2786 }, 2787 }} 2788 sbc1.SetResults(result1) 2789 result, err := executorExec(executor, "select main1.col, t.id1 from main1 join (select u1.id id1, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1) as t", nil) 2790 require.NoError(t, err) 2791 wantQueries := []*querypb.BoundQuery{{ 2792 Sql: "select u1.id as id1, u1.col from `user` as u1 where 1 != 1", 2793 BindVariables: map[string]*querypb.BindVariable{}, 2794 }, { 2795 Sql: "select u2.id from `user` as u2 where 1 != 1", 2796 BindVariables: map[string]*querypb.BindVariable{ 2797 "u1_col": sqltypes.NullBindVariable, 2798 }, 2799 }} 2800 utils.MustMatch(t, wantQueries, sbc1.Queries) 2801 2802 wantResult := &sqltypes.Result{ 2803 Fields: []*querypb.Field{ 2804 {Name: "col", Type: sqltypes.Int32}, 2805 {Name: "id", Type: sqltypes.Int32}, 2806 }, 2807 } 2808 if !result.Equal(wantResult) { 2809 t.Errorf("result: %+v, want %+v", result, wantResult) 2810 } 2811 } 2812 2813 func TestSelectBindvarswithPrepare(t *testing.T) { 2814 executor, sbc1, sbc2, _ := createExecutorEnv() 2815 logChan := QueryLogger.Subscribe("Test") 2816 defer QueryLogger.Unsubscribe(logChan) 2817 2818 sql := "select id from user where id = :id" 2819 _, err := executorPrepare(executor, sql, map[string]*querypb.BindVariable{ 2820 "id": sqltypes.Int64BindVariable(1), 2821 }) 2822 require.NoError(t, err) 2823 2824 wantQueries := []*querypb.BoundQuery{{ 2825 Sql: "select id from `user` where 1 != 1", 2826 BindVariables: map[string]*querypb.BindVariable{"id": sqltypes.Int64BindVariable(1)}, 2827 }} 2828 utils.MustMatch(t, wantQueries, sbc1.Queries) 2829 if sbc2.Queries != nil { 2830 t.Errorf("sbc2.Queries: %+v, want nil\n", sbc2.Queries) 2831 } 2832 } 2833 2834 func TestSelectDatabasePrepare(t *testing.T) { 2835 executor, _, _, _ := createExecutorEnv() 2836 executor.normalize = true 2837 logChan := QueryLogger.Subscribe("Test") 2838 defer QueryLogger.Unsubscribe(logChan) 2839 2840 sql := "select database()" 2841 _, err := executorPrepare(executor, sql, map[string]*querypb.BindVariable{}) 2842 require.NoError(t, err) 2843 } 2844 2845 func TestSelectWithUnionAll(t *testing.T) { 2846 executor, sbc1, sbc2, _ := createExecutorEnv() 2847 executor.normalize = true 2848 sql := "select id from user where id in (1, 2, 3) union all select id from user where id in (1, 2, 3)" 2849 bv, _ := sqltypes.BuildBindVariable([]int64{1, 2, 3}) 2850 bv1, _ := sqltypes.BuildBindVariable([]int64{1, 2}) 2851 bv2, _ := sqltypes.BuildBindVariable([]int64{3}) 2852 sbc1WantQueries := []*querypb.BoundQuery{{ 2853 Sql: "select id from `user` where id in ::__vals", 2854 BindVariables: map[string]*querypb.BindVariable{ 2855 "__vals": bv1, 2856 "vtg1": bv, 2857 "vtg2": bv, 2858 }, 2859 }, { 2860 Sql: "select id from `user` where id in ::__vals", 2861 BindVariables: map[string]*querypb.BindVariable{ 2862 "__vals": bv1, 2863 "vtg1": bv, 2864 "vtg2": bv, 2865 }, 2866 }} 2867 sbc2WantQueries := []*querypb.BoundQuery{{ 2868 Sql: "select id from `user` where id in ::__vals", 2869 BindVariables: map[string]*querypb.BindVariable{ 2870 "__vals": bv2, 2871 "vtg1": bv, 2872 "vtg2": bv, 2873 }, 2874 }, { 2875 Sql: "select id from `user` where id in ::__vals", 2876 BindVariables: map[string]*querypb.BindVariable{ 2877 "__vals": bv2, 2878 "vtg1": bv, 2879 "vtg2": bv, 2880 }, 2881 }} 2882 _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) 2883 require.NoError(t, err) 2884 utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "sbc1") 2885 utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "sbc2") 2886 2887 // Reset 2888 sbc1.Queries = nil 2889 sbc2.Queries = nil 2890 2891 _, err = executorStream(executor, sql) 2892 require.NoError(t, err) 2893 utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "sbc1") 2894 utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "sbc2") 2895 } 2896 2897 func TestSelectLock(t *testing.T) { 2898 executor, sbc1, _, _ := createExecutorEnv() 2899 session := NewSafeSession(nil) 2900 session.Session.InTransaction = true 2901 session.ShardSessions = []*vtgatepb.Session_ShardSession{{ 2902 Target: &querypb.Target{ 2903 Keyspace: "TestExecutor", 2904 Shard: "-20", 2905 TabletType: topodatapb.TabletType_PRIMARY, 2906 }, 2907 TransactionId: 12345, 2908 TabletAlias: sbc1.Tablet().Alias, 2909 }} 2910 2911 wantQueries := []*querypb.BoundQuery{{ 2912 Sql: "select get_lock('lock name', 10) from dual", 2913 BindVariables: map[string]*querypb.BindVariable{}, 2914 }} 2915 wantSession := &vtgatepb.Session{ 2916 InTransaction: true, 2917 ShardSessions: []*vtgatepb.Session_ShardSession{{ 2918 Target: &querypb.Target{ 2919 Keyspace: "TestExecutor", 2920 Shard: "-20", 2921 TabletType: topodatapb.TabletType_PRIMARY, 2922 }, 2923 TransactionId: 12345, 2924 TabletAlias: sbc1.Tablet().Alias, 2925 }}, 2926 LockSession: &vtgatepb.Session_ShardSession{ 2927 Target: &querypb.Target{Keyspace: "TestExecutor", Shard: "-20", TabletType: topodatapb.TabletType_PRIMARY}, 2928 TabletAlias: sbc1.Tablet().Alias, 2929 ReservedId: 1, 2930 }, 2931 AdvisoryLock: map[string]int64{"lock name": 1}, 2932 FoundRows: 1, 2933 RowCount: -1, 2934 } 2935 2936 _, err := exec(executor, session, "select get_lock('lock name', 10) from dual") 2937 require.NoError(t, err) 2938 wantSession.LastLockHeartbeat = session.Session.LastLockHeartbeat // copying as this is current timestamp value. 2939 utils.MustMatch(t, wantSession, session.Session, "") 2940 utils.MustMatch(t, wantQueries, sbc1.Queries, "") 2941 2942 wantQueries = append(wantQueries, &querypb.BoundQuery{ 2943 Sql: "select release_lock('lock name') from dual", 2944 BindVariables: map[string]*querypb.BindVariable{}, 2945 }) 2946 wantSession.AdvisoryLock = nil 2947 wantSession.LockSession = nil 2948 2949 _, err = exec(executor, session, "select release_lock('lock name') from dual") 2950 require.NoError(t, err) 2951 wantSession.LastLockHeartbeat = session.Session.LastLockHeartbeat // copying as this is current timestamp value. 2952 utils.MustMatch(t, wantQueries, sbc1.Queries, "") 2953 utils.MustMatch(t, wantSession, session.Session, "") 2954 } 2955 2956 func TestLockReserve(t *testing.T) { 2957 // no connection should be reserved for these queries. 2958 tcases := []string{ 2959 "select is_free_lock('lock name') from dual", 2960 "select is_used_lock('lock name') from dual", 2961 "select release_all_locks() from dual", 2962 "select release_lock('lock name') from dual", 2963 } 2964 2965 executor, _, _, _ := createExecutorEnv() 2966 session := NewAutocommitSession(&vtgatepb.Session{}) 2967 2968 for _, sql := range tcases { 2969 t.Run(sql, func(t *testing.T) { 2970 _, err := exec(executor, session, sql) 2971 require.NoError(t, err) 2972 require.Nil(t, session.LockSession) 2973 }) 2974 } 2975 2976 // get_lock should reserve a connection. 2977 _, err := exec(executor, session, "select get_lock('lock name', 10) from dual") 2978 require.NoError(t, err) 2979 require.NotNil(t, session.LockSession) 2980 2981 } 2982 2983 func TestSelectFromInformationSchema(t *testing.T) { 2984 executor, sbc1, _, _ := createExecutorEnv() 2985 session := NewSafeSession(nil) 2986 2987 // check failure when trying to query two keyspaces 2988 _, err := exec(executor, session, "SELECT B.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES AS A, INFORMATION_SCHEMA.COLUMNS AS B WHERE A.TABLE_SCHEMA = 'TestExecutor' AND A.TABLE_SCHEMA = 'TestXBadSharding'") 2989 require.Error(t, err) 2990 require.Contains(t, err.Error(), "specifying two different database in the query is not supported") 2991 2992 // we pick a keyspace and query for table_schema = database(). should be routed to the picked keyspace 2993 session.TargetString = "TestExecutor" 2994 _, err = exec(executor, session, "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = database()") 2995 require.NoError(t, err) 2996 assert.Equal(t, sbc1.StringQueries(), []string{"select * from INFORMATION_SCHEMA.`TABLES` where TABLE_SCHEMA = database()"}) 2997 2998 // `USE TestXBadSharding` and then query info_schema about TestExecutor - should target TestExecutor and not use the default keyspace 2999 sbc1.Queries = nil 3000 session.TargetString = "TestXBadSharding" 3001 _, err = exec(executor, session, "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'TestExecutor'") 3002 require.NoError(t, err) 3003 assert.Equal(t, sbc1.StringQueries(), []string{"select * from INFORMATION_SCHEMA.`TABLES` where TABLE_SCHEMA = :__vtschemaname"}) 3004 } 3005 3006 func TestStreamOrderByLimitWithMultipleResults(t *testing.T) { 3007 // Special setup: Don't use createExecutorEnv. 3008 cell := "aa" 3009 hc := discovery.NewFakeHealthCheck(nil) 3010 s := createSandbox(KsTestSharded) 3011 s.VSchema = executorVSchema 3012 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 3013 serv := newSandboxForCells([]string{cell}) 3014 resolver := newTestResolver(hc, serv, cell) 3015 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 3016 count := 1 3017 for _, shard := range shards { 3018 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3019 sbc.SetResults([]*sqltypes.Result{ 3020 sqltypes.MakeTestResult(sqltypes.MakeTestFields("id|col|weight_string(id)", "int32|int32|varchar"), fmt.Sprintf("%d|%d|NULL", count, count)), 3021 sqltypes.MakeTestResult(sqltypes.MakeTestFields("id|col|weight_string(id)", "int32|int32|varchar"), fmt.Sprintf("%d|%d|NULL", count+10, count)), 3022 }) 3023 count++ 3024 } 3025 3026 executor := NewExecutor(context.Background(), serv, cell, resolver, true, false, testBufferSize, cache.DefaultConfig, nil, false, querypb.ExecuteOptions_V3) 3027 before := runtime.NumGoroutine() 3028 3029 query := "select id, col from user order by id limit 2" 3030 gotResult, err := executorStream(executor, query) 3031 require.NoError(t, err) 3032 3033 wantResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields("id|col", "int32|int32"), "1|1", "2|2") 3034 utils.MustMatch(t, wantResult, gotResult) 3035 // some sleep to close all goroutines. 3036 time.Sleep(100 * time.Millisecond) 3037 assert.GreaterOrEqual(t, before, runtime.NumGoroutine(), "left open goroutines lingering") 3038 } 3039 3040 func TestSelectScatterFails(t *testing.T) { 3041 sess := &vtgatepb.Session{} 3042 cell := "aa" 3043 hc := discovery.NewFakeHealthCheck(nil) 3044 s := createSandbox(KsTestSharded) 3045 s.VSchema = executorVSchema 3046 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 3047 serv := newSandboxForCells([]string{cell}) 3048 resolver := newTestResolver(hc, serv, cell) 3049 3050 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 3051 for i, shard := range shards { 3052 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3053 sbc.SetResults([]*sqltypes.Result{{ 3054 Fields: []*querypb.Field{ 3055 {Name: "col1", Type: sqltypes.Int32}, 3056 {Name: "col2", Type: sqltypes.Int32}, 3057 {Name: "weight_string(col2)", Type: sqltypes.VarBinary}, 3058 }, 3059 InsertID: 0, 3060 Rows: [][]sqltypes.Value{{ 3061 sqltypes.NewInt32(1), 3062 sqltypes.NewInt32(int32(i % 4)), 3063 sqltypes.NULL, 3064 }}, 3065 }}) 3066 } 3067 3068 executor := createExecutor(serv, cell, resolver) 3069 executor.allowScatter = false 3070 logChan := QueryLogger.Subscribe("Test") 3071 defer QueryLogger.Unsubscribe(logChan) 3072 3073 _, err := executorExecSession(executor, "select id from user", nil, sess) 3074 require.Error(t, err) 3075 assert.Contains(t, err.Error(), "scatter") 3076 3077 // Run the test again, to ensure it behaves the same for a cached query 3078 _, err = executorExecSession(executor, "select id from user", nil, sess) 3079 require.Error(t, err) 3080 assert.Contains(t, err.Error(), "scatter") 3081 3082 _, err = executorExecSession(executor, "select /*vt+ ALLOW_SCATTER */ id from user", nil, sess) 3083 require.NoError(t, err) 3084 3085 _, err = executorExecSession(executor, "begin", nil, sess) 3086 require.NoError(t, err) 3087 3088 _, err = executorExecSession(executor, "commit", nil, sess) 3089 require.NoError(t, err) 3090 3091 _, err = executorExecSession(executor, "savepoint a", nil, sess) 3092 require.NoError(t, err) 3093 } 3094 3095 func TestGen4SelectStraightJoin(t *testing.T) { 3096 executor, sbc1, _, _ := createExecutorEnv() 3097 executor.normalize = true 3098 executor.pv = querypb.ExecuteOptions_Gen4 3099 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 3100 query := "select u.id from user u straight_join user2 u2 on u.id = u2.id" 3101 _, err := executor.Execute(context.Background(), 3102 "TestGen4SelectStraightJoin", 3103 session, 3104 query, map[string]*querypb.BindVariable{}, 3105 ) 3106 require.NoError(t, err) 3107 wantQueries := []*querypb.BoundQuery{ 3108 { 3109 Sql: "select u.id from `user` as u, user2 as u2 where u.id = u2.id", 3110 BindVariables: map[string]*querypb.BindVariable{}, 3111 }, 3112 } 3113 wantWarnings := []*querypb.QueryWarning{ 3114 { 3115 Code: 1235, 3116 Message: "straight join is converted to normal join", 3117 }, 3118 } 3119 utils.MustMatch(t, wantQueries, sbc1.Queries) 3120 utils.MustMatch(t, wantWarnings, session.Warnings) 3121 } 3122 3123 func TestGen4MultiColumnVindexEqual(t *testing.T) { 3124 executor, sbc1, sbc2, _ := createExecutorEnv() 3125 executor.normalize = true 3126 executor.pv = querypb.ExecuteOptions_Gen4 3127 3128 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 3129 query := "select * from user_region where cola = 1 and colb = 2" 3130 _, err := executor.Execute(context.Background(), 3131 "TestGen4MultiColumnVindex", 3132 session, 3133 query, map[string]*querypb.BindVariable{}, 3134 ) 3135 require.NoError(t, err) 3136 wantQueries := []*querypb.BoundQuery{ 3137 { 3138 Sql: "select * from user_region where cola = :cola and colb = :colb", 3139 BindVariables: map[string]*querypb.BindVariable{ 3140 "cola": sqltypes.Int64BindVariable(1), 3141 "colb": sqltypes.Int64BindVariable(2), 3142 }, 3143 }, 3144 } 3145 utils.MustMatch(t, wantQueries, sbc1.Queries) 3146 require.Nil(t, sbc2.Queries) 3147 3148 sbc1.Queries = nil 3149 3150 query = "select * from user_region where cola = 17984 and colb = 1" 3151 _, err = executor.Execute(context.Background(), 3152 "TestGen4MultiColumnVindex", 3153 session, 3154 query, map[string]*querypb.BindVariable{}, 3155 ) 3156 require.NoError(t, err) 3157 wantQueries = []*querypb.BoundQuery{ 3158 { 3159 Sql: "select * from user_region where cola = :cola and colb = :colb", 3160 BindVariables: map[string]*querypb.BindVariable{ 3161 "cola": sqltypes.Int64BindVariable(17984), 3162 "colb": sqltypes.Int64BindVariable(1), 3163 }, 3164 }, 3165 } 3166 utils.MustMatch(t, wantQueries, sbc2.Queries) 3167 require.Nil(t, sbc1.Queries) 3168 } 3169 3170 func TestGen4MultiColumnVindexIn(t *testing.T) { 3171 executor, sbc1, sbc2, _ := createExecutorEnv() 3172 executor.normalize = true 3173 executor.pv = querypb.ExecuteOptions_Gen4 3174 3175 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 3176 query := "select * from user_region where cola IN (1,17984) and colb IN (2,3,4)" 3177 _, err := executor.Execute(context.Background(), 3178 "TestGen4MultiColumnVindex", 3179 session, 3180 query, map[string]*querypb.BindVariable{}, 3181 ) 3182 require.NoError(t, err) 3183 bv1, _ := sqltypes.BuildBindVariable([]int64{1}) 3184 bv2, _ := sqltypes.BuildBindVariable([]int64{17984}) 3185 bvtg1, _ := sqltypes.BuildBindVariable([]int64{1, 17984}) 3186 bvtg2, _ := sqltypes.BuildBindVariable([]int64{2, 3, 4}) 3187 wantQueries := []*querypb.BoundQuery{ 3188 { 3189 Sql: "select * from user_region where cola in ::__vals0 and colb in ::__vals1", 3190 BindVariables: map[string]*querypb.BindVariable{ 3191 "__vals0": bv1, 3192 "__vals1": bvtg2, 3193 "vtg1": bvtg1, 3194 "vtg2": bvtg2, 3195 }, 3196 }, 3197 } 3198 utils.MustMatch(t, wantQueries, sbc1.Queries) 3199 wantQueries = []*querypb.BoundQuery{ 3200 { 3201 Sql: "select * from user_region where cola in ::__vals0 and colb in ::__vals1", 3202 BindVariables: map[string]*querypb.BindVariable{ 3203 "__vals0": bv2, 3204 "__vals1": bvtg2, 3205 "vtg1": bvtg1, 3206 "vtg2": bvtg2, 3207 }, 3208 }, 3209 } 3210 utils.MustMatch(t, wantQueries, sbc2.Queries) 3211 } 3212 3213 func TestGen4MultiColMixedColComparision(t *testing.T) { 3214 executor, sbc1, sbc2, _ := createExecutorEnv() 3215 executor.normalize = true 3216 executor.pv = querypb.ExecuteOptions_Gen4 3217 3218 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 3219 query := "select * from user_region where colb = 2 and cola IN (1,17984)" 3220 _, err := executor.Execute(context.Background(), 3221 "TestGen4MultiColMixedColComparision", 3222 session, 3223 query, map[string]*querypb.BindVariable{}, 3224 ) 3225 require.NoError(t, err) 3226 bvtg1 := sqltypes.Int64BindVariable(2) 3227 bvtg2, _ := sqltypes.BuildBindVariable([]int64{1, 17984}) 3228 vals0sbc1, _ := sqltypes.BuildBindVariable([]int64{1}) 3229 vals0sbc2, _ := sqltypes.BuildBindVariable([]int64{17984}) 3230 wantQueries := []*querypb.BoundQuery{ 3231 { 3232 Sql: "select * from user_region where colb = :colb and cola in ::__vals0", 3233 BindVariables: map[string]*querypb.BindVariable{ 3234 "__vals0": vals0sbc1, 3235 "colb": bvtg1, 3236 "vtg1": bvtg2, 3237 }, 3238 }, 3239 } 3240 utils.MustMatch(t, wantQueries, sbc1.Queries) 3241 wantQueries = []*querypb.BoundQuery{ 3242 { 3243 Sql: "select * from user_region where colb = :colb and cola in ::__vals0", 3244 BindVariables: map[string]*querypb.BindVariable{ 3245 "__vals0": vals0sbc2, 3246 "colb": bvtg1, 3247 "vtg1": bvtg2, 3248 }, 3249 }, 3250 } 3251 utils.MustMatch(t, wantQueries, sbc2.Queries) 3252 } 3253 3254 func TestGen4MultiColBestVindexSel(t *testing.T) { 3255 executor, sbc1, sbc2, _ := createExecutorEnv() 3256 executor.normalize = true 3257 executor.pv = querypb.ExecuteOptions_Gen4 3258 3259 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 3260 query := "select * from user_region where colb = 2 and cola IN (1,17984) and cola = 1" 3261 _, err := executor.Execute(context.Background(), 3262 "TestGen4MultiColBestVindexSel", 3263 session, 3264 query, map[string]*querypb.BindVariable{}, 3265 ) 3266 require.NoError(t, err) 3267 bvtg2, _ := sqltypes.BuildBindVariable([]int64{1, 17984}) 3268 wantQueries := []*querypb.BoundQuery{ 3269 { 3270 Sql: "select * from user_region where colb = :colb and cola in ::vtg1 and cola = :cola", 3271 BindVariables: map[string]*querypb.BindVariable{ 3272 "colb": sqltypes.Int64BindVariable(2), 3273 "vtg1": bvtg2, 3274 "cola": sqltypes.Int64BindVariable(1), 3275 }, 3276 }, 3277 } 3278 utils.MustMatch(t, wantQueries, sbc1.Queries) 3279 require.Nil(t, sbc2.Queries) 3280 3281 // reset 3282 sbc1.Queries = nil 3283 3284 query = "select * from user_region where colb in (10,20) and cola IN (1,17984) and cola = 1 and colb = 2" 3285 _, err = executor.Execute(context.Background(), 3286 "TestGen4MultiColBestVindexSel", 3287 session, 3288 query, map[string]*querypb.BindVariable{}, 3289 ) 3290 require.NoError(t, err) 3291 3292 bvtg1, _ := sqltypes.BuildBindVariable([]int64{10, 20}) 3293 wantQueries = []*querypb.BoundQuery{ 3294 { 3295 Sql: "select * from user_region where colb in ::vtg1 and cola in ::vtg2 and cola = :cola and colb = :colb", 3296 BindVariables: map[string]*querypb.BindVariable{ 3297 "vtg1": bvtg1, 3298 "vtg2": bvtg2, 3299 "cola": sqltypes.Int64BindVariable(1), 3300 "colb": sqltypes.Int64BindVariable(2), 3301 }, 3302 }, 3303 } 3304 utils.MustMatch(t, wantQueries, sbc1.Queries) 3305 require.Nil(t, sbc2.Queries) 3306 } 3307 3308 func TestGen4MultiColMultiEqual(t *testing.T) { 3309 executor, sbc1, sbc2, _ := createExecutorEnv() 3310 executor.normalize = true 3311 executor.pv = querypb.ExecuteOptions_Gen4 3312 3313 session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) 3314 query := "select * from user_region where (cola,colb) in ((17984,2),(17984,3))" 3315 _, err := executor.Execute(context.Background(), 3316 "TestGen4MultiColMultiEqual", 3317 session, 3318 query, map[string]*querypb.BindVariable{}, 3319 ) 3320 require.NoError(t, err) 3321 wantQueries := []*querypb.BoundQuery{ 3322 { 3323 Sql: "select * from user_region where (cola, colb) in ((:vtg1, :vtg2), (:vtg1, :vtg3))", 3324 BindVariables: map[string]*querypb.BindVariable{ 3325 "vtg1": sqltypes.Int64BindVariable(17984), 3326 "vtg2": sqltypes.Int64BindVariable(2), 3327 "vtg3": sqltypes.Int64BindVariable(3), 3328 }, 3329 }, 3330 } 3331 require.Nil(t, sbc1.Queries) 3332 utils.MustMatch(t, wantQueries, sbc2.Queries) 3333 } 3334 3335 func TestGen4SelectUnqualifiedReferenceTable(t *testing.T) { 3336 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 3337 executor.pv = querypb.ExecuteOptions_Gen4 3338 3339 query := "select * from zip_detail" 3340 _, err := executorExec(executor, query, nil) 3341 require.NoError(t, err) 3342 wantQueries := []*querypb.BoundQuery{ 3343 { 3344 Sql: query, 3345 BindVariables: map[string]*querypb.BindVariable{}, 3346 }, 3347 } 3348 utils.MustMatch(t, wantQueries, sbclookup.Queries) 3349 require.Nil(t, sbc1.Queries) 3350 require.Nil(t, sbc2.Queries) 3351 } 3352 3353 func TestGen4SelectQualifiedReferenceTable(t *testing.T) { 3354 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 3355 executor.pv = querypb.ExecuteOptions_Gen4 3356 3357 query := fmt.Sprintf("select * from %s.zip_detail", KsTestSharded) 3358 _, err := executorExec(executor, query, nil) 3359 require.NoError(t, err) 3360 wantQueries := []*querypb.BoundQuery{ 3361 { 3362 Sql: "select * from zip_detail", 3363 BindVariables: map[string]*querypb.BindVariable{}, 3364 }, 3365 } 3366 require.Nil(t, sbclookup.Queries) 3367 utils.MustMatch(t, wantQueries, sbc1.Queries) 3368 require.Nil(t, sbc2.Queries) 3369 } 3370 3371 func TestGen4JoinUnqualifiedReferenceTable(t *testing.T) { 3372 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 3373 executor.pv = querypb.ExecuteOptions_Gen4 3374 3375 query := "select * from user join zip_detail on user.zip_detail_id = zip_detail.id" 3376 _, err := executorExec(executor, query, nil) 3377 require.NoError(t, err) 3378 wantQueries := []*querypb.BoundQuery{ 3379 { 3380 Sql: "select * from `user`, zip_detail where `user`.zip_detail_id = zip_detail.id", 3381 BindVariables: map[string]*querypb.BindVariable{}, 3382 }, 3383 } 3384 require.Nil(t, sbclookup.Queries) 3385 utils.MustMatch(t, wantQueries, sbc1.Queries) 3386 utils.MustMatch(t, wantQueries, sbc2.Queries) 3387 3388 sbc1.Queries = nil 3389 sbc2.Queries = nil 3390 3391 query = "select * from simple join zip_detail on simple.zip_detail_id = zip_detail.id" 3392 _, err = executorExec(executor, query, nil) 3393 require.NoError(t, err) 3394 wantQueries = []*querypb.BoundQuery{ 3395 { 3396 Sql: "select * from `simple` join zip_detail on `simple`.zip_detail_id = zip_detail.id", 3397 BindVariables: map[string]*querypb.BindVariable{}, 3398 }, 3399 } 3400 utils.MustMatch(t, wantQueries, sbclookup.Queries) 3401 require.Nil(t, sbc1.Queries) 3402 require.Nil(t, sbc2.Queries) 3403 } 3404 3405 func TestGen4CrossShardJoinQualifiedReferenceTable(t *testing.T) { 3406 executor, sbc1, sbc2, sbclookup := createExecutorEnv() 3407 executor.pv = querypb.ExecuteOptions_Gen4 3408 3409 query := "select user.id from user join TestUnsharded.zip_detail on user.zip_detail_id = TestUnsharded.zip_detail.id" 3410 _, err := executorExec(executor, query, nil) 3411 require.NoError(t, err) 3412 3413 shardedWantQueries := []*querypb.BoundQuery{ 3414 { 3415 Sql: "select `user`.id from `user`, zip_detail where `user`.zip_detail_id = zip_detail.id", 3416 BindVariables: map[string]*querypb.BindVariable{}, 3417 }, 3418 } 3419 require.Nil(t, sbclookup.Queries) 3420 utils.MustMatch(t, shardedWantQueries, sbc1.Queries) 3421 utils.MustMatch(t, shardedWantQueries, sbc2.Queries) 3422 3423 sbclookup.Queries = nil 3424 sbc1.Queries = nil 3425 sbc2.Queries = nil 3426 3427 query = "select simple.id from simple join TestExecutor.zip_detail on simple.zip_detail_id = TestExecutor.zip_detail.id" 3428 _, err = executorExec(executor, query, nil) 3429 require.NoError(t, err) 3430 unshardedWantQueries := []*querypb.BoundQuery{ 3431 { 3432 Sql: "select `simple`.id from `simple` join zip_detail on `simple`.zip_detail_id = zip_detail.id", 3433 BindVariables: map[string]*querypb.BindVariable{}, 3434 }, 3435 } 3436 utils.MustMatch(t, unshardedWantQueries, sbclookup.Queries) 3437 require.Nil(t, sbc1.Queries) 3438 require.Nil(t, sbc2.Queries) 3439 } 3440 3441 func TestRegionRange(t *testing.T) { 3442 // Special setup: Don't use createExecutorEnv. 3443 3444 cell := "regioncell" 3445 ks := "TestExecutor" 3446 hc := discovery.NewFakeHealthCheck(nil) 3447 s := createSandbox(ks) 3448 s.ShardSpec = "-20-20a0-" 3449 s.VSchema = executorVSchema 3450 serv := newSandboxForCells([]string{cell}) 3451 resolver := newTestResolver(hc, serv, cell) 3452 shards := []string{"-20", "20-20a0", "20a0-"} 3453 var conns []*sandboxconn.SandboxConn 3454 for _, shard := range shards { 3455 sbc := hc.AddTestTablet(cell, shard, 1, ks, shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3456 conns = append(conns, sbc) 3457 } 3458 executor := createExecutor(serv, cell, resolver) 3459 executor.pv = querypb.ExecuteOptions_Gen4 3460 3461 tcases := []struct { 3462 regionID int 3463 noOfShardsTouched int 3464 }{{ 3465 regionID: 31, 3466 noOfShardsTouched: 1, 3467 }, { 3468 regionID: 32, 3469 noOfShardsTouched: 2, 3470 }, { 3471 regionID: 33, 3472 noOfShardsTouched: 1, 3473 }} 3474 for _, tcase := range tcases { 3475 t.Run(strconv.Itoa(tcase.regionID), func(t *testing.T) { 3476 sql := fmt.Sprintf("select * from user_region where cola = %d", tcase.regionID) 3477 _, err := executor.Execute( 3478 context.Background(), 3479 "TestRegionRange", 3480 NewAutocommitSession(&vtgatepb.Session{}), 3481 sql, 3482 nil) 3483 require.NoError(t, err) 3484 count := 0 3485 for _, sbc := range conns { 3486 count = count + len(sbc.Queries) 3487 sbc.Queries = nil 3488 } 3489 require.Equal(t, tcase.noOfShardsTouched, count) 3490 }) 3491 } 3492 } 3493 3494 func TestMultiCol(t *testing.T) { 3495 // Special setup: Don't use createLegacyExecutorEnv. 3496 cell := "multicol" 3497 ks := "TestMultiCol" 3498 hc := discovery.NewFakeHealthCheck(nil) 3499 s := createSandbox(ks) 3500 s.ShardSpec = "-20-20a0-" 3501 s.VSchema = multiColVschema 3502 serv := newSandboxForCells([]string{cell}) 3503 resolver := newTestResolver(hc, serv, cell) 3504 shards := []string{"-20", "20-20a0", "20a0-"} 3505 var conns []*sandboxconn.SandboxConn 3506 for _, shard := range shards { 3507 sbc := hc.AddTestTablet(cell, shard, 1, ks, shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3508 conns = append(conns, sbc) 3509 } 3510 executor := createExecutor(serv, cell, resolver) 3511 executor.pv = querypb.ExecuteOptions_Gen4 3512 3513 tcases := []struct { 3514 cola, colb, colc int 3515 shards []string 3516 }{{ 3517 cola: 202, colb: 1, colc: 1, 3518 shards: []string{"-20"}, 3519 }, { 3520 cola: 203, colb: 1, colc: 1, 3521 shards: []string{"20-20a0"}, 3522 }, { 3523 cola: 204, colb: 1, colc: 1, 3524 shards: []string{"20a0-"}, 3525 }} 3526 3527 ctx := context.Background() 3528 session := NewAutocommitSession(&vtgatepb.Session{}) 3529 3530 for _, tcase := range tcases { 3531 t.Run(fmt.Sprintf("%d_%d_%d", tcase.cola, tcase.colb, tcase.colc), func(t *testing.T) { 3532 sql := fmt.Sprintf("select * from multicoltbl where cola = %d and colb = %d and colc = '%d'", tcase.cola, tcase.colb, tcase.colc) 3533 _, err := executor.Execute(ctx, "TestMultiCol", session, sql, nil) 3534 require.NoError(t, err) 3535 var shards []string 3536 for _, sbc := range conns { 3537 if len(sbc.Queries) > 0 { 3538 shards = append(shards, sbc.Tablet().Shard) 3539 sbc.Queries = nil 3540 } 3541 } 3542 require.Equal(t, tcase.shards, shards) 3543 }) 3544 } 3545 } 3546 3547 var multiColVschema = ` 3548 { 3549 "sharded": true, 3550 "vindexes": { 3551 "multicol_vdx": { 3552 "type": "multicol", 3553 "params": { 3554 "column_count": "3", 3555 "column_bytes": "1,3,4", 3556 "column_vindex": "hash,binary,unicode_loose_xxhash" 3557 } 3558 } 3559 }, 3560 "tables": { 3561 "multicoltbl": { 3562 "column_vindexes": [ 3563 { 3564 "columns": ["cola","colb","colc"], 3565 "name": "multicol_vdx" 3566 } 3567 ] 3568 } 3569 } 3570 } 3571 ` 3572 3573 func TestMultiColPartial(t *testing.T) { 3574 // Special setup: Don't use createLegacyExecutorEnv. 3575 cell := "multicol" 3576 ks := "TestMultiCol" 3577 hc := discovery.NewFakeHealthCheck(nil) 3578 s := createSandbox(ks) 3579 s.ShardSpec = "-20-20a0c0-" 3580 s.VSchema = multiColVschema 3581 serv := newSandboxForCells([]string{cell}) 3582 resolver := newTestResolver(hc, serv, cell) 3583 shards := []string{"-20", "20-20a0c0", "20a0c0-"} 3584 var conns []*sandboxconn.SandboxConn 3585 for _, shard := range shards { 3586 sbc := hc.AddTestTablet(cell, shard, 1, ks, shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3587 conns = append(conns, sbc) 3588 } 3589 executor := createExecutor(serv, cell, resolver) 3590 executor.pv = querypb.ExecuteOptions_Gen4 3591 3592 tcases := []struct { 3593 where string 3594 shards []string 3595 }{{ 3596 where: "cola = 252", 3597 shards: []string{"-20"}, 3598 }, { 3599 where: "cola = 289", 3600 shards: []string{"20a0c0-"}, 3601 }, { 3602 where: "cola = 606", 3603 shards: []string{"20-20a0c0", "20a0c0-"}, 3604 }, { 3605 where: "cola = 606 and colb = _binary '\x1f'", 3606 shards: []string{"20-20a0c0"}, 3607 }, { 3608 where: "cola = 606 and colb = _binary '\xa0'", 3609 shards: []string{"20-20a0c0", "20a0c0-"}, 3610 }, { 3611 where: "cola = 606 and colb = _binary '\xa1'", 3612 shards: []string{"20a0c0-"}, 3613 }} 3614 3615 ctx := context.Background() 3616 session := NewAutocommitSession(&vtgatepb.Session{}) 3617 3618 for _, tcase := range tcases { 3619 t.Run(tcase.where, func(t *testing.T) { 3620 sql := fmt.Sprintf("select * from multicoltbl where %s", tcase.where) 3621 _, err := executor.Execute(ctx, "TestMultiCol", session, sql, nil) 3622 require.NoError(t, err) 3623 var shards []string 3624 for _, sbc := range conns { 3625 if len(sbc.Queries) > 0 { 3626 shards = append(shards, sbc.Tablet().Shard) 3627 sbc.Queries = nil 3628 } 3629 } 3630 require.Equal(t, tcase.shards, shards) 3631 }) 3632 } 3633 } 3634 3635 func TestSelectAggregationNoData(t *testing.T) { 3636 // Special setup: Don't use createExecutorEnv. 3637 cell := "aa" 3638 hc := discovery.NewFakeHealthCheck(nil) 3639 createSandbox(KsTestSharded).VSchema = executorVSchema 3640 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 3641 serv := newSandboxForCells([]string{cell}) 3642 resolver := newTestResolver(hc, serv, cell) 3643 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 3644 var conns []*sandboxconn.SandboxConn 3645 for _, shard := range shards { 3646 sbc := hc.AddTestTablet(cell, shard, 1, KsTestSharded, shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3647 conns = append(conns, sbc) 3648 } 3649 executor := createExecutor(serv, cell, resolver) 3650 executor.pv = querypb.ExecuteOptions_Gen4 3651 3652 tcases := []struct { 3653 sql string 3654 sandboxRes *sqltypes.Result 3655 expSandboxQ string 3656 expField string 3657 expRow string 3658 }{ 3659 { 3660 sql: `select count(distinct col) from user`, 3661 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64")), 3662 expSandboxQ: "select col, weight_string(col) from `user` group by col, weight_string(col) order by col asc", 3663 expField: `[name:"count(distinct col)" type:INT64]`, 3664 expRow: `[[INT64(0)]]`, 3665 }, 3666 { 3667 sql: `select count(*) from user`, 3668 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("count(*)", "int64"), "0"), 3669 expSandboxQ: "select count(*) from `user`", 3670 expField: `[name:"count(*)" type:INT64]`, 3671 expRow: `[[INT64(0)]]`, 3672 }, 3673 { 3674 sql: `select col, count(*) from user group by col`, 3675 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col|count(*)", "int64|int64")), 3676 expSandboxQ: "select col, count(*), weight_string(col) from `user` group by col, weight_string(col) order by col asc", 3677 expField: `[name:"col" type:INT64 name:"count(*)" type:INT64]`, 3678 expRow: `[]`, 3679 }, 3680 { 3681 sql: `select col, count(*) from user group by col limit 2`, 3682 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col|count(*)", "int64|int64")), 3683 expSandboxQ: "select col, count(*), weight_string(col) from `user` group by col, weight_string(col) order by col asc limit :__upper_limit", 3684 expField: `[name:"col" type:INT64 name:"count(*)" type:INT64]`, 3685 expRow: `[]`, 3686 }, 3687 { 3688 sql: `select count(*) from (select col1, col2 from user limit 2) x`, 3689 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2", "int64|int64")), 3690 expSandboxQ: "select col1, col2 from `user` limit :__upper_limit", 3691 expField: `[name:"count(*)" type:INT64]`, 3692 expRow: `[[INT64(0)]]`, 3693 }, 3694 { 3695 sql: `select col2, count(*) from (select col1, col2 from user limit 2) x group by col2`, 3696 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col2)", "int64|int64|varbinary")), 3697 expSandboxQ: "select col1, col2, weight_string(col2) from `user` order by col2 asc limit :__upper_limit", 3698 expField: `[name:"col2" type:INT64 name:"count(*)" type:INT64]`, 3699 expRow: `[]`, 3700 }, 3701 } 3702 3703 for _, tc := range tcases { 3704 t.Run(tc.sql, func(t *testing.T) { 3705 for _, sbc := range conns { 3706 sbc.SetResults([]*sqltypes.Result{tc.sandboxRes}) 3707 sbc.Queries = nil 3708 } 3709 qr, err := executorExec(executor, tc.sql, nil) 3710 require.NoError(t, err) 3711 assert.Equal(t, tc.expField, fmt.Sprintf("%v", qr.Fields)) 3712 assert.Equal(t, tc.expRow, fmt.Sprintf("%v", qr.Rows)) 3713 require.Len(t, conns[0].Queries, 1) 3714 assert.Equal(t, tc.expSandboxQ, conns[0].Queries[0].Sql) 3715 }) 3716 } 3717 } 3718 3719 func TestSelectAggregationData(t *testing.T) { 3720 // Special setup: Don't use createExecutorEnv. 3721 cell := "aa" 3722 hc := discovery.NewFakeHealthCheck(nil) 3723 createSandbox(KsTestSharded).VSchema = executorVSchema 3724 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 3725 serv := newSandboxForCells([]string{cell}) 3726 resolver := newTestResolver(hc, serv, cell) 3727 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 3728 var conns []*sandboxconn.SandboxConn 3729 for _, shard := range shards { 3730 sbc := hc.AddTestTablet(cell, shard, 1, KsTestSharded, shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3731 conns = append(conns, sbc) 3732 } 3733 executor := createExecutor(serv, cell, resolver) 3734 executor.pv = querypb.ExecuteOptions_Gen4 3735 3736 tcases := []struct { 3737 sql string 3738 sandboxRes *sqltypes.Result 3739 expSandboxQ string 3740 expField string 3741 expRow string 3742 }{ 3743 { 3744 sql: `select count(distinct col) from user`, 3745 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col|weight_string(col)", "int64|varbinary"), "1|NULL", "2|NULL", "2|NULL", "3|NULL"), 3746 expSandboxQ: "select col, weight_string(col) from `user` group by col, weight_string(col) order by col asc", 3747 expField: `[name:"count(distinct col)" type:INT64]`, 3748 expRow: `[[INT64(3)]]`, 3749 }, 3750 { 3751 sql: `select count(*) from user`, 3752 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("count(*)", "int64"), "3"), 3753 expSandboxQ: "select count(*) from `user`", 3754 expField: `[name:"count(*)" type:INT64]`, 3755 expRow: `[[INT64(24)]]`, 3756 }, 3757 { 3758 sql: `select col, count(*) from user group by col`, 3759 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col|count(*)|weight_string(col)", "int64|int64|varbinary"), "1|3|NULL"), 3760 expSandboxQ: "select col, count(*), weight_string(col) from `user` group by col, weight_string(col) order by col asc", 3761 expField: `[name:"col" type:INT64 name:"count(*)" type:INT64]`, 3762 expRow: `[[INT64(1) INT64(24)]]`, 3763 }, 3764 { 3765 sql: `select col, count(*) from user group by col limit 2`, 3766 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col|count(*)|weight_string(col)", "int64|int64|varbinary"), "1|2|NULL", "2|1|NULL", "3|4|NULL"), 3767 expSandboxQ: "select col, count(*), weight_string(col) from `user` group by col, weight_string(col) order by col asc limit :__upper_limit", 3768 expField: `[name:"col" type:INT64 name:"count(*)" type:INT64]`, 3769 expRow: `[[INT64(1) INT64(16)] [INT64(2) INT64(8)]]`, 3770 }, 3771 { 3772 sql: `select count(*) from (select col1, col2 from user limit 2) x`, 3773 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2", "int64|int64"), "1|2", "2|1"), 3774 expSandboxQ: "select col1, col2 from `user` limit :__upper_limit", 3775 expField: `[name:"count(*)" type:INT64]`, 3776 expRow: `[[INT64(2)]]`, 3777 }, 3778 { 3779 sql: `select col2, count(*) from (select col1, col2 from user limit 9) x group by col2`, 3780 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col2)", "int64|int64|varbinary"), "3|1|NULL", "2|2|NULL"), 3781 expSandboxQ: "select col1, col2, weight_string(col2) from `user` order by col2 asc limit :__upper_limit", 3782 expField: `[name:"col2" type:INT64 name:"count(*)" type:INT64]`, 3783 expRow: `[[INT64(1) INT64(8)] [INT64(2) INT64(1)]]`, 3784 }, 3785 { 3786 sql: `select count(col1) from (select id, col1 from user limit 2) x`, 3787 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("id|col1", "int64|varchar"), "3|a", "2|b"), 3788 expSandboxQ: "select id, col1 from `user` limit :__upper_limit", 3789 expField: `[name:"count(col1)" type:INT64]`, 3790 expRow: `[[INT64(2)]]`, 3791 }, 3792 { 3793 sql: `select count(col1), col2 from (select col2, col1 from user limit 9) x group by col2`, 3794 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col2|col1|weight_string(col2)", "int64|varchar|varbinary"), "3|a|NULL", "2|b|NULL"), 3795 expSandboxQ: "select col2, col1, weight_string(col2) from `user` order by col2 asc limit :__upper_limit", 3796 expField: `[name:"count(col1)" type:INT64 name:"col2" type:INT64]`, 3797 expRow: `[[INT64(8) INT64(2)] [INT64(1) INT64(3)]]`, 3798 }, 3799 { 3800 sql: `select col1, count(col2) from (select col1, col2 from user limit 9) x group by col1`, 3801 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|int64|varbinary"), "a|1|a", "b|null|b"), 3802 expSandboxQ: "select col1, col2, weight_string(col1) from `user` order by col1 asc limit :__upper_limit", 3803 expField: `[name:"col1" type:VARCHAR name:"count(col2)" type:INT64]`, 3804 expRow: `[[VARCHAR("a") INT64(8)] [VARCHAR("b") INT64(0)]]`, 3805 }, 3806 { 3807 sql: `select col1, count(col2) from (select col1, col2 from user limit 32) x group by col1`, 3808 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|int64|varbinary"), "null|1|null", "null|null|null", "a|1|a", "b|null|b"), 3809 expSandboxQ: "select col1, col2, weight_string(col1) from `user` order by col1 asc limit :__upper_limit", 3810 expField: `[name:"col1" type:VARCHAR name:"count(col2)" type:INT64]`, 3811 expRow: `[[NULL INT64(8)] [VARCHAR("a") INT64(8)] [VARCHAR("b") INT64(0)]]`, 3812 }, 3813 { 3814 sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, 3815 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|int64|varbinary"), "a|3|a"), 3816 expSandboxQ: "select col1, col2, weight_string(col1) from `user` order by col1 asc limit :__upper_limit", 3817 expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:DECIMAL]`, 3818 expRow: `[[VARCHAR("a") DECIMAL(12)]]`, 3819 }, 3820 { 3821 sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, 3822 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|varchar|varbinary"), "a|2|a"), 3823 expSandboxQ: "select col1, col2, weight_string(col1) from `user` order by col1 asc limit :__upper_limit", 3824 expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:DECIMAL]`, 3825 expRow: `[[VARCHAR("a") DECIMAL(8)]]`, 3826 }, 3827 { 3828 sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, 3829 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|varchar|varbinary"), "a|x|a"), 3830 expSandboxQ: "select col1, col2, weight_string(col1) from `user` order by col1 asc limit :__upper_limit", 3831 expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:DECIMAL]`, 3832 expRow: `[[VARCHAR("a") DECIMAL(0)]]`, 3833 }, 3834 { 3835 sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, 3836 sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|varchar|varbinary"), "a|null|a"), 3837 expSandboxQ: "select col1, col2, weight_string(col1) from `user` order by col1 asc limit :__upper_limit", 3838 expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:DECIMAL]`, 3839 expRow: `[[VARCHAR("a") NULL]]`, 3840 }, 3841 } 3842 3843 for _, tc := range tcases { 3844 t.Run(tc.sql, func(t *testing.T) { 3845 for _, sbc := range conns { 3846 sbc.SetResults([]*sqltypes.Result{tc.sandboxRes}) 3847 sbc.Queries = nil 3848 } 3849 qr, err := executorExec(executor, tc.sql, nil) 3850 require.NoError(t, err) 3851 assert.Equal(t, tc.expField, fmt.Sprintf("%v", qr.Fields)) 3852 assert.Equal(t, tc.expRow, fmt.Sprintf("%v", qr.Rows)) 3853 require.Len(t, conns[0].Queries, 1) 3854 assert.Equal(t, tc.expSandboxQ, conns[0].Queries[0].Sql) 3855 }) 3856 } 3857 } 3858 3859 func TestSelectAggregationRandom(t *testing.T) { 3860 cell := "aa" 3861 hc := discovery.NewFakeHealthCheck(nil) 3862 createSandbox(KsTestSharded).VSchema = executorVSchema 3863 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 3864 serv := newSandboxForCells([]string{cell}) 3865 resolver := newTestResolver(hc, serv, cell) 3866 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 3867 var conns []*sandboxconn.SandboxConn 3868 for _, shard := range shards { 3869 sbc := hc.AddTestTablet(cell, shard, 1, KsTestSharded, shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 3870 conns = append(conns, sbc) 3871 3872 sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( 3873 sqltypes.MakeTestFields("a|b", "int64|int64"), 3874 "null|null", 3875 )}) 3876 } 3877 3878 conns[0].SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( 3879 sqltypes.MakeTestFields("a|b", "int64|int64"), 3880 "10|1", 3881 )}) 3882 3883 executor := createExecutor(serv, cell, resolver) 3884 executor.pv = querypb.ExecuteOptions_Gen4 3885 session := NewAutocommitSession(&vtgatepb.Session{}) 3886 3887 rs, err := executor.Execute(context.Background(), "TestSelectCFC", session, 3888 "select /*vt+ PLANNER=gen4 */ A.a, A.b, (A.a / A.b) as c from (select sum(a) as a, sum(b) as b from user) A", nil) 3889 require.NoError(t, err) 3890 assert.Equal(t, `[[INT64(10) INT64(1) DECIMAL(10.0000)]]`, fmt.Sprintf("%v", rs.Rows)) 3891 } 3892 3893 func TestSelectHexAndBit(t *testing.T) { 3894 executor, _, _, _ := createExecutorEnv() 3895 executor.normalize = true 3896 session := NewAutocommitSession(&vtgatepb.Session{}) 3897 3898 qr, err := executor.Execute(context.Background(), "TestSelectHexAndBit", session, 3899 "select 0b1001, b'1001', 0x9, x'09'", nil) 3900 require.NoError(t, err) 3901 require.Equal(t, `[[VARBINARY("\t") VARBINARY("\t") VARBINARY("\t") VARBINARY("\t")]]`, fmt.Sprintf("%v", qr.Rows)) 3902 3903 qr, err = executor.Execute(context.Background(), "TestSelectHexAndBit", session, 3904 "select 1 + 0b1001, 1 + b'1001', 1 + 0x9, 1 + x'09'", nil) 3905 require.NoError(t, err) 3906 require.Equal(t, `[[UINT64(10) UINT64(10) UINT64(10) UINT64(10)]]`, fmt.Sprintf("%v", qr.Rows)) 3907 } 3908 3909 // TestSelectCFC tests validates that cfc vindex plan gets cached and same plan is getting reused. 3910 // This also validates that cache_size is able to calculate the cfc vindex plan size. 3911 func TestSelectCFC(t *testing.T) { 3912 executor, _, _, _ := createExecutorEnv() 3913 executor.normalize = true 3914 session := NewAutocommitSession(&vtgatepb.Session{}) 3915 3916 _, err := executor.Execute(context.Background(), "TestSelectCFC", session, 3917 "select /*vt+ PLANNER=gen4 */ c2 from tbl_cfc where c1 like 'A%'", nil) 3918 require.NoError(t, err) 3919 3920 timeout := time.After(10 * time.Second) 3921 for { 3922 select { 3923 case <-timeout: 3924 t.Fatal("not able to cache a plan withing 10 seconds.") 3925 case <-time.After(5 * time.Millisecond): 3926 // should be able to find cache entry before the timeout. 3927 cacheItems := executor.debugCacheEntries() 3928 for _, item := range cacheItems { 3929 if strings.Contains(item.Key, "c2 from tbl_cfc where c1 like") { 3930 return 3931 } 3932 } 3933 } 3934 } 3935 } 3936 3937 func TestSelectView(t *testing.T) { 3938 executor, sbc, _, _ := createExecutorEnv() 3939 // add the view to local vschema 3940 err := executor.vschema.AddView(KsTestSharded, "user_details_view", "select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id") 3941 require.NoError(t, err) 3942 3943 executor.normalize = true 3944 session := NewAutocommitSession(&vtgatepb.Session{}) 3945 3946 _, err = executor.Execute(context.Background(), "TestSelectView", session, 3947 "select * from user_details_view", nil) 3948 require.NoError(t, err) 3949 wantQueries := []*querypb.BoundQuery{{ 3950 Sql: "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view", 3951 BindVariables: map[string]*querypb.BindVariable{}, 3952 }} 3953 utils.MustMatch(t, wantQueries, sbc.Queries) 3954 3955 sbc.Queries = nil 3956 _, err = executor.Execute(context.Background(), "TestSelectView", session, 3957 "select * from user_details_view where id = 2", nil) 3958 require.NoError(t, err) 3959 wantQueries = []*querypb.BoundQuery{{ 3960 Sql: "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view where id = :id", 3961 BindVariables: map[string]*querypb.BindVariable{ 3962 "id": sqltypes.Int64BindVariable(2), 3963 }, 3964 }} 3965 utils.MustMatch(t, wantQueries, sbc.Queries) 3966 3967 sbc.Queries = nil 3968 _, err = executor.Execute(context.Background(), "TestSelectView", session, 3969 "select * from user_details_view where id in (1,2,3,4,5)", nil) 3970 require.NoError(t, err) 3971 bvtg1, _ := sqltypes.BuildBindVariable([]int64{1, 2, 3, 4, 5}) 3972 bvals, _ := sqltypes.BuildBindVariable([]int64{1, 2}) 3973 wantQueries = []*querypb.BoundQuery{{ 3974 Sql: "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view where id in ::__vals", 3975 BindVariables: map[string]*querypb.BindVariable{ 3976 "vtg1": bvtg1, 3977 "__vals": bvals, 3978 }, 3979 }} 3980 utils.MustMatch(t, wantQueries, sbc.Queries) 3981 } 3982 3983 func TestMain(m *testing.M) { 3984 _flag.ParseFlagsForTest() 3985 os.Exit(m.Run()) 3986 }