vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/query_executor_test.go (about)

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