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  }