vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/unsharded/main_test.go (about) 1 /* 2 Copyright 2020 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 unsharded 18 19 import ( 20 "context" 21 "flag" 22 "os" 23 "testing" 24 "time" 25 26 "vitess.io/vitess/go/test/endtoend/utils" 27 28 "vitess.io/vitess/go/vt/log" 29 querypb "vitess.io/vitess/go/vt/proto/query" 30 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 34 "vitess.io/vitess/go/mysql" 35 "vitess.io/vitess/go/sqltypes" 36 "vitess.io/vitess/go/test/endtoend/cluster" 37 ) 38 39 var ( 40 clusterInstance *cluster.LocalProcessCluster 41 cell = "zone1" 42 hostname = "localhost" 43 KeyspaceName = "customer" 44 SchemaSQL = ` 45 CREATE TABLE t1 ( 46 c1 BIGINT NOT NULL, 47 c2 BIGINT NOT NULL, 48 c3 BIGINT, 49 c4 varchar(100), 50 PRIMARY KEY (c1), 51 UNIQUE KEY (c2), 52 UNIQUE KEY (c3), 53 UNIQUE KEY (c4) 54 ) ENGINE=Innodb; 55 56 CREATE TABLE allDefaults ( 57 id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, 58 name VARCHAR(255) 59 ) ENGINE=Innodb;` 60 VSchema = ` 61 { 62 "sharded": false, 63 "tables": { 64 "t1": { 65 "columns": [ 66 { 67 "name": "c1", 68 "type": "INT64" 69 }, 70 { 71 "name": "c2", 72 "type": "INT64" 73 }, 74 { 75 "name": "c3", 76 "type": "INT64" 77 }, 78 { 79 "name": "c4", 80 "type": "VARCHAR" 81 } 82 ] 83 }, 84 "allDefaults": { 85 "columns": [ 86 { 87 "name": "id", 88 "type": "INT64" 89 }, 90 { 91 "name": "name", 92 "type": "VARCHAR" 93 } 94 ] 95 } 96 } 97 } 98 ` 99 100 createProcSQL = `use vt_customer; 101 CREATE PROCEDURE sp_insert() 102 BEGIN 103 insert into allDefaults () values (); 104 END; 105 106 CREATE PROCEDURE sp_delete() 107 BEGIN 108 delete from allDefaults; 109 END; 110 111 CREATE PROCEDURE sp_multi_dml() 112 BEGIN 113 insert into allDefaults () values (); 114 delete from allDefaults; 115 END; 116 117 CREATE PROCEDURE sp_variable() 118 BEGIN 119 insert into allDefaults () values (); 120 SELECT min(id) INTO @myvar FROM allDefaults; 121 DELETE FROM allDefaults WHERE id = @myvar; 122 END; 123 124 CREATE PROCEDURE sp_select() 125 BEGIN 126 SELECT * FROM allDefaults; 127 END; 128 129 CREATE PROCEDURE sp_all() 130 BEGIN 131 insert into allDefaults () values (); 132 select * from allDefaults; 133 delete from allDefaults; 134 set autocommit = 0; 135 END; 136 137 CREATE PROCEDURE in_parameter(IN val int) 138 BEGIN 139 insert into allDefaults(id) values(val); 140 END; 141 142 CREATE PROCEDURE out_parameter(OUT val int) 143 BEGIN 144 insert into allDefaults(id) values (128); 145 select 128 into val from dual; 146 END; 147 ` 148 ) 149 150 var enableSettingsPool bool 151 152 func TestMain(m *testing.M) { 153 defer cluster.PanicHandler(nil) 154 flag.Parse() 155 156 code := runAllTests(m) 157 if code != 0 { 158 os.Exit(code) 159 } 160 161 println("running with settings pool enabled") 162 // run again with settings pool enabled. 163 enableSettingsPool = true 164 code = runAllTests(m) 165 os.Exit(code) 166 } 167 168 func runAllTests(m *testing.M) int { 169 clusterInstance = cluster.NewCluster(cell, hostname) 170 defer clusterInstance.Teardown() 171 172 // Start topo server 173 if err := clusterInstance.StartTopo(); err != nil { 174 return 1 175 } 176 177 // Start keyspace 178 Keyspace := &cluster.Keyspace{ 179 Name: KeyspaceName, 180 SchemaSQL: SchemaSQL, 181 VSchema: VSchema, 182 } 183 clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-transaction-timeout", "3", "--queryserver-config-max-result-size", "30"} 184 if enableSettingsPool { 185 clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--queryserver-enable-settings-pool") 186 } 187 if err := clusterInstance.StartUnshardedKeyspace(*Keyspace, 0, false); err != nil { 188 log.Fatal(err.Error()) 189 return 1 190 } 191 192 // Start vtgate 193 clusterInstance.VtGateExtraArgs = []string{"--warn_sharded_only=true"} 194 if err := clusterInstance.StartVtgate(); err != nil { 195 log.Fatal(err.Error()) 196 return 1 197 } 198 199 primaryTablet := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess 200 if _, err := primaryTablet.QueryTablet(createProcSQL, KeyspaceName, false); err != nil { 201 log.Fatal(err.Error()) 202 return 1 203 } 204 205 return m.Run() 206 } 207 208 func TestSelectIntoAndLoadFrom(t *testing.T) { 209 // Test is skipped because it requires secure-file-priv variable to be set to not NULL or empty. 210 t.Skip() 211 defer cluster.PanicHandler(t) 212 ctx := context.Background() 213 vtParams := mysql.ConnParams{ 214 Host: "localhost", 215 Port: clusterInstance.VtgateMySQLPort, 216 } 217 conn, err := mysql.Connect(ctx, &vtParams) 218 require.Nil(t, err) 219 defer conn.Close() 220 221 defer utils.Exec(t, conn, `delete from t1`) 222 utils.Exec(t, conn, `insert into t1(c1, c2, c3, c4) values (300,100,300,'abc')`) 223 res := utils.Exec(t, conn, `select @@secure_file_priv;`) 224 directory := res.Rows[0][0].ToString() 225 query := `select * from t1 into outfile '` + directory + `x.txt'` 226 utils.Exec(t, conn, query) 227 defer os.Remove(directory + `x.txt`) 228 query = `load data infile '` + directory + `x.txt' into table t1` 229 utils.AssertContainsError(t, conn, query, "Duplicate entry '300' for key 'PRIMARY'") 230 utils.Exec(t, conn, `delete from t1`) 231 utils.Exec(t, conn, query) 232 utils.AssertMatches(t, conn, `select c1,c2,c3 from t1`, `[[INT64(300) INT64(100) INT64(300)]]`) 233 query = `select * from t1 into dumpfile '` + directory + `x1.txt'` 234 utils.Exec(t, conn, query) 235 defer os.Remove(directory + `x1.txt`) 236 query = `select * from t1 into outfile '` + directory + `x2.txt' Fields terminated by ';' optionally enclosed by '"' escaped by '\t' lines terminated by '\n'` 237 utils.Exec(t, conn, query) 238 defer os.Remove(directory + `x2.txt`) 239 query = `load data infile '` + directory + `x2.txt' replace into table t1 Fields terminated by ';' optionally enclosed by '"' escaped by '\t' lines terminated by '\n'` 240 utils.Exec(t, conn, query) 241 utils.AssertMatches(t, conn, `select c1,c2,c3 from t1`, `[[INT64(300) INT64(100) INT64(300)]]`) 242 utils.AssertMatches(t, conn, "show warnings", `[[VARCHAR("Warning") UINT16(1235) VARCHAR("use of feature that is only supported in unsharded mode: LOAD")]]`) 243 } 244 245 func TestEmptyStatement(t *testing.T) { 246 defer cluster.PanicHandler(t) 247 ctx := context.Background() 248 vtParams := mysql.ConnParams{ 249 Host: "localhost", 250 Port: clusterInstance.VtgateMySQLPort, 251 } 252 conn, err := mysql.Connect(ctx, &vtParams) 253 require.Nil(t, err) 254 defer conn.Close() 255 defer utils.Exec(t, conn, `delete from t1`) 256 utils.AssertContainsError(t, conn, " \t; \n;", "Query was empty") 257 execMulti(t, conn, `insert into t1(c1, c2, c3, c4) values (300,100,300,'abc'); ;; insert into t1(c1, c2, c3, c4) values (301,101,301,'abcd');;`) 258 259 utils.AssertMatches(t, conn, `select c1,c2,c3 from t1`, `[[INT64(300) INT64(100) INT64(300)] [INT64(301) INT64(101) INT64(301)]]`) 260 } 261 262 func TestTopoDownServingQuery(t *testing.T) { 263 defer cluster.PanicHandler(t) 264 ctx := context.Background() 265 vtParams := mysql.ConnParams{ 266 Host: "localhost", 267 Port: clusterInstance.VtgateMySQLPort, 268 } 269 conn, err := mysql.Connect(ctx, &vtParams) 270 require.Nil(t, err) 271 defer conn.Close() 272 273 defer utils.Exec(t, conn, `delete from t1`) 274 275 execMulti(t, conn, `insert into t1(c1, c2, c3, c4) values (300,100,300,'abc'); ;; insert into t1(c1, c2, c3, c4) values (301,101,301,'abcd');;`) 276 utils.AssertMatches(t, conn, `select c1,c2,c3 from t1`, `[[INT64(300) INT64(100) INT64(300)] [INT64(301) INT64(101) INT64(301)]]`) 277 clusterInstance.TopoProcess.TearDown(clusterInstance.Cell, clusterInstance.OriginalVTDATAROOT, clusterInstance.CurrentVTDATAROOT, true, *clusterInstance.TopoFlavorString()) 278 time.Sleep(3 * time.Second) 279 utils.AssertMatches(t, conn, `select c1,c2,c3 from t1`, `[[INT64(300) INT64(100) INT64(300)] [INT64(301) INT64(101) INT64(301)]]`) 280 } 281 282 func TestInsertAllDefaults(t *testing.T) { 283 defer cluster.PanicHandler(t) 284 ctx := context.Background() 285 vtParams := mysql.ConnParams{ 286 Host: "localhost", 287 Port: clusterInstance.VtgateMySQLPort, 288 } 289 conn, err := mysql.Connect(ctx, &vtParams) 290 require.NoError(t, err) 291 defer conn.Close() 292 293 utils.Exec(t, conn, `insert into allDefaults () values ()`) 294 utils.AssertMatches(t, conn, `select * from allDefaults`, "[[INT64(1) NULL]]") 295 } 296 297 func TestDDLUnsharded(t *testing.T) { 298 defer cluster.PanicHandler(t) 299 ctx := context.Background() 300 vtParams := mysql.ConnParams{ 301 Host: "localhost", 302 Port: clusterInstance.VtgateMySQLPort, 303 } 304 conn, err := mysql.Connect(ctx, &vtParams) 305 require.NoError(t, err) 306 defer conn.Close() 307 308 utils.Exec(t, conn, `create table tempt1(c1 BIGINT NOT NULL,c2 BIGINT NOT NULL,c3 BIGINT,c4 varchar(100),PRIMARY KEY (c1), UNIQUE KEY (c2),UNIQUE KEY (c3), UNIQUE KEY (c4))`) 309 // Test that create view works and the output is as expected 310 utils.Exec(t, conn, `create view v1 as select * from tempt1`) 311 utils.Exec(t, conn, `insert into tempt1(c1, c2, c3, c4) values (300,100,300,'abc'),(30,10,30,'ac'),(3,0,3,'a')`) 312 utils.AssertMatches(t, conn, "select * from v1", `[[INT64(3) INT64(0) INT64(3) VARCHAR("a")] [INT64(30) INT64(10) INT64(30) VARCHAR("ac")] [INT64(300) INT64(100) INT64(300) VARCHAR("abc")]]`) 313 utils.Exec(t, conn, `drop view v1`) 314 utils.Exec(t, conn, `drop table tempt1`) 315 utils.AssertMatchesAny(t, conn, "show tables", `[[VARBINARY("allDefaults")] [VARBINARY("t1")]]`, `[[VARCHAR("allDefaults")] [VARCHAR("t1")]]`) 316 } 317 318 func TestCallProcedure(t *testing.T) { 319 defer cluster.PanicHandler(t) 320 ctx := context.Background() 321 vtParams := mysql.ConnParams{ 322 Host: "localhost", 323 Port: clusterInstance.VtgateMySQLPort, 324 Flags: mysql.CapabilityClientMultiResults, 325 DbName: "@primary", 326 } 327 time.Sleep(5 * time.Second) 328 conn, err := mysql.Connect(ctx, &vtParams) 329 require.NoError(t, err) 330 defer conn.Close() 331 qr := utils.Exec(t, conn, `CALL sp_insert()`) 332 require.EqualValues(t, 1, qr.RowsAffected) 333 334 utils.AssertMatches(t, conn, "show warnings", `[[VARCHAR("Warning") UINT16(1235) VARCHAR("'CALL' not supported in sharded mode")]]`) 335 336 _, err = conn.ExecuteFetch(`CALL sp_select()`, 1000, true) 337 require.Error(t, err) 338 require.Contains(t, err.Error(), "Multi-Resultset not supported in stored procedure") 339 340 _, err = conn.ExecuteFetch(`CALL sp_all()`, 1000, true) 341 require.Error(t, err) 342 require.Contains(t, err.Error(), "Multi-Resultset not supported in stored procedure") 343 344 qr = utils.Exec(t, conn, `CALL sp_delete()`) 345 require.GreaterOrEqual(t, 1, int(qr.RowsAffected)) 346 347 qr = utils.Exec(t, conn, `CALL sp_multi_dml()`) 348 require.EqualValues(t, 1, qr.RowsAffected) 349 350 qr = utils.Exec(t, conn, `CALL sp_variable()`) 351 require.EqualValues(t, 1, qr.RowsAffected) 352 353 qr = utils.Exec(t, conn, `CALL in_parameter(42)`) 354 require.EqualValues(t, 1, qr.RowsAffected) 355 356 _ = utils.Exec(t, conn, `SET @foo = 123`) 357 qr = utils.Exec(t, conn, `CALL in_parameter(@foo)`) 358 require.EqualValues(t, 1, qr.RowsAffected) 359 qr = utils.Exec(t, conn, "select * from allDefaults where id = 123") 360 assert.NotEmpty(t, qr.Rows) 361 362 _, err = conn.ExecuteFetch(`CALL out_parameter(@foo)`, 100, true) 363 require.Error(t, err) 364 require.Contains(t, err.Error(), "OUT and INOUT parameters are not supported") 365 } 366 367 func TestTempTable(t *testing.T) { 368 defer cluster.PanicHandler(t) 369 ctx := context.Background() 370 vtParams := mysql.ConnParams{ 371 Host: "localhost", 372 Port: clusterInstance.VtgateMySQLPort, 373 } 374 conn1, err := mysql.Connect(ctx, &vtParams) 375 require.NoError(t, err) 376 defer conn1.Close() 377 378 _ = utils.Exec(t, conn1, `create temporary table temp_t(id bigint primary key)`) 379 utils.AssertMatches(t, conn1, "show warnings", `[[VARCHAR("Warning") UINT16(1235) VARCHAR("'temporary table' not supported in sharded mode")]]`) 380 _ = utils.Exec(t, conn1, `insert into temp_t(id) values (1),(2),(3)`) 381 utils.AssertMatches(t, conn1, `select id from temp_t order by id`, `[[INT64(1)] [INT64(2)] [INT64(3)]]`) 382 utils.AssertMatches(t, conn1, `select count(table_id) from information_schema.innodb_temp_table_info`, `[[INT64(1)]]`) 383 384 conn2, err := mysql.Connect(ctx, &vtParams) 385 require.NoError(t, err) 386 defer conn2.Close() 387 388 utils.AssertMatches(t, conn2, `select count(table_id) from information_schema.innodb_temp_table_info`, `[[INT64(1)]]`) 389 utils.AssertContainsError(t, conn2, `show create table temp_t`, `Table 'vt_customer.temp_t' doesn't exist (errno 1146) (sqlstate 42S02)`) 390 } 391 392 func TestReservedConnDML(t *testing.T) { 393 defer cluster.PanicHandler(t) 394 ctx := context.Background() 395 vtParams := mysql.ConnParams{ 396 Host: "localhost", 397 Port: clusterInstance.VtgateMySQLPort, 398 } 399 conn, err := mysql.Connect(ctx, &vtParams) 400 require.NoError(t, err) 401 defer conn.Close() 402 403 utils.Exec(t, conn, `set default_week_format = 1`) 404 utils.Exec(t, conn, `begin`) 405 utils.Exec(t, conn, `insert into allDefaults () values ()`) 406 utils.Exec(t, conn, `commit`) 407 408 time.Sleep(6 * time.Second) 409 410 utils.Exec(t, conn, `begin`) 411 utils.Exec(t, conn, `insert into allDefaults () values ()`) 412 utils.Exec(t, conn, `commit`) 413 } 414 415 func TestNumericPrecisionScale(t *testing.T) { 416 defer cluster.PanicHandler(t) 417 ctx := context.Background() 418 vtParams := mysql.ConnParams{ 419 Host: "localhost", 420 Port: clusterInstance.VtgateMySQLPort, 421 } 422 conn, err := mysql.Connect(ctx, &vtParams) 423 require.NoError(t, err) 424 defer conn.Close() 425 426 _ = utils.Exec(t, conn, "CREATE TABLE `a` (`one` bigint NOT NULL PRIMARY KEY) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4") 427 require.NoError(t, err) 428 defer utils.Exec(t, conn, "drop table `a`") 429 430 qr := utils.Exec(t, conn, "select numeric_precision, numeric_scale from information_schema.columns where table_name = 'a'") 431 require.Equal(t, 1, len(qr.Rows)) 432 433 /* 434 We expect UINT64 to be returned as type for field and rows from VTGate to client. 435 436 require.Equal(t, querypb.Type_UINT64, qr.Fields[0].Type) 437 require.Equal(t, querypb.Type_UINT64, qr.Fields[1].Type) 438 require.Equal(t, sqltypes.Uint64, qr.Rows[0][0].Type()) 439 require.Equal(t, sqltypes.Uint64, qr.Rows[0][1].Type()) 440 441 But, the field query from mysql returns field at UINT32 and row types as UINT64. 442 Our conversion on VTGate on receiving data from VTTablet the Rows are converted to Field Types. 443 So, we see UINT32 for both fields and rows. 444 445 This issue is only with MySQL 8.0. In CI we use 5.7 as well. So asserting with both the values. 446 */ 447 448 assert.True(t, qr.Fields[0].Type == querypb.Type_UINT64 || qr.Fields[0].Type == querypb.Type_UINT32) 449 assert.True(t, qr.Fields[1].Type == querypb.Type_UINT64 || qr.Fields[1].Type == querypb.Type_UINT32) 450 assert.True(t, qr.Rows[0][0].Type() == sqltypes.Uint64 || qr.Rows[0][0].Type() == sqltypes.Uint32) 451 assert.True(t, qr.Rows[0][1].Type() == sqltypes.Uint64 || qr.Rows[0][1].Type() == sqltypes.Uint32) 452 } 453 454 func TestDeleteAlias(t *testing.T) { 455 vtParams := mysql.ConnParams{ 456 Host: "localhost", 457 Port: clusterInstance.VtgateMySQLPort, 458 } 459 conn, err := mysql.Connect(context.Background(), &vtParams) 460 require.NoError(t, err) 461 defer conn.Close() 462 463 utils.Exec(t, conn, "delete t1 from t1 where c1 = 1") 464 utils.Exec(t, conn, "delete t.* from t1 t where t.c1 = 1") 465 } 466 467 func TestFloatValueDefault(t *testing.T) { 468 vtParams := mysql.ConnParams{ 469 Host: "localhost", 470 Port: clusterInstance.VtgateMySQLPort, 471 } 472 conn, err := mysql.Connect(context.Background(), &vtParams) 473 require.NoError(t, err) 474 defer conn.Close() 475 476 utils.Exec(t, conn, `create table test_float_default (pos_f float default 2.1, neg_f float default -2.1);`) 477 defer utils.Exec(t, conn, `drop table test_float_default`) 478 utils.AssertMatchesAny(t, conn, "select table_name, column_name, column_default from information_schema.columns where table_name = 'test_float_default' order by column_default desc", 479 `[[VARBINARY("test_float_default") VARCHAR("pos_f") BLOB("2.1")] [VARBINARY("test_float_default") VARCHAR("neg_f") BLOB("-2.1")]]`, 480 `[[VARCHAR("test_float_default") VARCHAR("pos_f") TEXT("2.1")] [VARCHAR("test_float_default") VARCHAR("neg_f") TEXT("-2.1")]]`) 481 } 482 483 func execMulti(t *testing.T, conn *mysql.Conn, query string) []*sqltypes.Result { 484 t.Helper() 485 var res []*sqltypes.Result 486 qr, more, err := conn.ExecuteFetchMulti(query, 1000, true) 487 res = append(res, qr) 488 require.NoError(t, err) 489 for more == true { 490 qr, more, _, err = conn.ReadQueryResult(1000, true) 491 require.NoError(t, err) 492 res = append(res, qr) 493 } 494 return res 495 }