vitess.io/vitess@v0.16.2/go/vt/vtgate/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 vtgate
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"html/template"
    25  	"net/http"
    26  	"net/http/httptest"
    27  	"reflect"
    28  	"sort"
    29  	"strings"
    30  	"testing"
    31  
    32  	"vitess.io/vitess/go/vt/vtgate/logstats"
    33  
    34  	"google.golang.org/protobuf/proto"
    35  
    36  	"vitess.io/vitess/go/cache"
    37  	"vitess.io/vitess/go/test/utils"
    38  	"vitess.io/vitess/go/vt/vtgate/engine"
    39  
    40  	"vitess.io/vitess/go/vt/topo"
    41  
    42  	"github.com/google/go-cmp/cmp"
    43  
    44  	"vitess.io/vitess/go/mysql"
    45  	"vitess.io/vitess/go/sqltypes"
    46  	"vitess.io/vitess/go/vt/callerid"
    47  	"vitess.io/vitess/go/vt/sqlparser"
    48  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    49  	"vitess.io/vitess/go/vt/vtgate/vschemaacl"
    50  
    51  	querypb "vitess.io/vitess/go/vt/proto/query"
    52  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    53  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    54  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    55  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    56  
    57  	"github.com/stretchr/testify/assert"
    58  	"github.com/stretchr/testify/require"
    59  )
    60  
    61  func TestExecutorResultsExceeded(t *testing.T) {
    62  	save := warnMemoryRows
    63  	warnMemoryRows = 3
    64  	defer func() { warnMemoryRows = save }()
    65  
    66  	executor, _, _, sbclookup := createExecutorEnv()
    67  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"})
    68  
    69  	initial := warnings.Counts()["ResultsExceeded"]
    70  
    71  	result1 := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1")
    72  	result2 := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1", "2", "3", "4")
    73  	sbclookup.SetResults([]*sqltypes.Result{result1, result2})
    74  
    75  	_, err := executor.Execute(ctx, "TestExecutorResultsExceeded", session, "select * from main1", nil)
    76  	require.NoError(t, err)
    77  	assert.Equal(t, initial, warnings.Counts()["ResultsExceeded"], "warnings count")
    78  
    79  	_, err = executor.Execute(ctx, "TestExecutorResultsExceeded", session, "select * from main1", nil)
    80  	require.NoError(t, err)
    81  	assert.Equal(t, initial+1, warnings.Counts()["ResultsExceeded"], "warnings count")
    82  }
    83  
    84  func TestExecutorMaxMemoryRowsExceeded(t *testing.T) {
    85  	save := maxMemoryRows
    86  	maxMemoryRows = 3
    87  	defer func() { maxMemoryRows = save }()
    88  
    89  	executor, _, _, sbclookup := createExecutorEnv()
    90  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"})
    91  	result := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1", "2", "3", "4")
    92  	fn := func(r *sqltypes.Result) error {
    93  		return nil
    94  	}
    95  	testCases := []struct {
    96  		query string
    97  		err   string
    98  	}{
    99  		{"select /*vt+ IGNORE_MAX_MEMORY_ROWS=1 */ * from main1", ""},
   100  		{"select * from main1", "in-memory row count exceeded allowed limit of 3"},
   101  	}
   102  
   103  	for _, test := range testCases {
   104  		sbclookup.SetResults([]*sqltypes.Result{result})
   105  		stmt, err := sqlparser.Parse(test.query)
   106  		require.NoError(t, err)
   107  
   108  		_, err = executor.Execute(ctx, "TestExecutorMaxMemoryRowsExceeded", session, test.query, nil)
   109  		if sqlparser.IgnoreMaxMaxMemoryRowsDirective(stmt) {
   110  			require.NoError(t, err, "no error when DirectiveIgnoreMaxMemoryRows is provided")
   111  		} else {
   112  			assert.EqualError(t, err, test.err, "maxMemoryRows limit exceeded")
   113  		}
   114  
   115  		sbclookup.SetResults([]*sqltypes.Result{result})
   116  		err = executor.StreamExecute(ctx, "TestExecutorMaxMemoryRowsExceeded", session, test.query, nil, fn)
   117  		require.NoError(t, err, "maxMemoryRows limit does not apply to StreamExecute")
   118  	}
   119  }
   120  
   121  func TestExecutorTransactionsNoAutoCommit(t *testing.T) {
   122  	executor, _, _, sbclookup := createExecutorEnv()
   123  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"})
   124  
   125  	logChan := QueryLogger.Subscribe("Test")
   126  	defer QueryLogger.Unsubscribe(logChan)
   127  
   128  	// begin.
   129  	_, err := executor.Execute(ctx, "TestExecute", session, "begin", nil)
   130  	require.NoError(t, err)
   131  	wantSession := &vtgatepb.Session{InTransaction: true, TargetString: "@primary", SessionUUID: "suuid"}
   132  	utils.MustMatch(t, wantSession, session.Session, "session")
   133  	assert.EqualValues(t, 0, sbclookup.CommitCount.Get(), "commit count")
   134  	logStats := testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0)
   135  	assert.EqualValues(t, 0, logStats.CommitTime, "logstats: expected zero CommitTime")
   136  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   137  
   138  	// commit.
   139  	_, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil)
   140  	require.NoError(t, err)
   141  	logStats = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1)
   142  	assert.EqualValues(t, 0, logStats.CommitTime, "logstats: expected zero CommitTime")
   143  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   144  
   145  	_, err = executor.Execute(context.Background(), "TestExecute", session, "commit", nil)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	wantSession = &vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"}
   150  	if !proto.Equal(session.Session, wantSession) {
   151  		t.Errorf("begin: %v, want %v", session.Session, wantSession)
   152  	}
   153  	if commitCount := sbclookup.CommitCount.Get(); commitCount != 1 {
   154  		t.Errorf("want 1, got %d", commitCount)
   155  	}
   156  	logStats = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1)
   157  	if logStats.CommitTime == 0 {
   158  		t.Errorf("logstats: expected non-zero CommitTime")
   159  	}
   160  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   161  
   162  	// rollback.
   163  	_, err = executor.Execute(ctx, "TestExecute", session, "begin", nil)
   164  	require.NoError(t, err)
   165  	_, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil)
   166  	require.NoError(t, err)
   167  	_, err = executor.Execute(ctx, "TestExecute", session, "rollback", nil)
   168  	require.NoError(t, err)
   169  	wantSession = &vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"}
   170  	utils.MustMatch(t, wantSession, session.Session, "session")
   171  	assert.EqualValues(t, 1, sbclookup.RollbackCount.Get(), "rollback count")
   172  	_ = testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0)
   173  	_ = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1)
   174  	logStats = testQueryLog(t, logChan, "TestExecute", "ROLLBACK", "rollback", 1)
   175  	if logStats.CommitTime == 0 {
   176  		t.Errorf("logstats: expected non-zero CommitTime")
   177  	}
   178  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   179  
   180  	// CloseSession doesn't log anything
   181  	err = executor.CloseSession(ctx, session)
   182  	require.NoError(t, err)
   183  	logStats = getQueryLog(logChan)
   184  	if logStats != nil {
   185  		t.Errorf("logstats: expected no record for no-op rollback, got %v", logStats)
   186  	}
   187  
   188  	// Prevent use of non-primary if in_transaction is on.
   189  	session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary", InTransaction: true})
   190  	_, err = executor.Execute(ctx, "TestExecute", session, "use @replica", nil)
   191  	require.EqualError(t, err, `can't execute the given command because you have an active transaction`)
   192  }
   193  
   194  func TestDirectTargetRewrites(t *testing.T) {
   195  	executor, _, _, sbclookup := createExecutorEnv()
   196  	executor.normalize = true
   197  
   198  	session := &vtgatepb.Session{
   199  		TargetString:    "TestUnsharded/0@primary",
   200  		Autocommit:      true,
   201  		TransactionMode: vtgatepb.TransactionMode_MULTI,
   202  	}
   203  	sql := "select database()"
   204  
   205  	_, err := executor.Execute(ctx, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{})
   206  	require.NoError(t, err)
   207  	assertQueries(t, sbclookup, []*querypb.BoundQuery{{
   208  		Sql:           "select :__vtdbname as `database()` from dual",
   209  		BindVariables: map[string]*querypb.BindVariable{"__vtdbname": sqltypes.StringBindVariable("TestUnsharded/0@primary")},
   210  	}})
   211  }
   212  
   213  func TestExecutorTransactionsAutoCommit(t *testing.T) {
   214  	executor, _, _, sbclookup := createExecutorEnv()
   215  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"})
   216  
   217  	logChan := QueryLogger.Subscribe("Test")
   218  	defer QueryLogger.Unsubscribe(logChan)
   219  
   220  	// begin.
   221  	_, err := executor.Execute(ctx, "TestExecute", session, "begin", nil)
   222  	require.NoError(t, err)
   223  	wantSession := &vtgatepb.Session{InTransaction: true, TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}
   224  	utils.MustMatch(t, wantSession, session.Session, "session")
   225  	if commitCount := sbclookup.CommitCount.Get(); commitCount != 0 {
   226  		t.Errorf("want 0, got %d", commitCount)
   227  	}
   228  	logStats := testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0)
   229  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   230  
   231  	// commit.
   232  	_, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil)
   233  	require.NoError(t, err)
   234  	_, err = executor.Execute(ctx, "TestExecute", session, "commit", nil)
   235  	require.NoError(t, err)
   236  	wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}
   237  	utils.MustMatch(t, wantSession, session.Session, "session")
   238  	assert.EqualValues(t, 1, sbclookup.CommitCount.Get())
   239  
   240  	logStats = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1)
   241  	assert.EqualValues(t, 0, logStats.CommitTime)
   242  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   243  	logStats = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1)
   244  	assert.NotEqual(t, 0, logStats.CommitTime)
   245  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   246  
   247  	// rollback.
   248  	_, err = executor.Execute(ctx, "TestExecute", session, "begin", nil)
   249  	require.NoError(t, err)
   250  	_, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil)
   251  	require.NoError(t, err)
   252  	_, err = executor.Execute(ctx, "TestExecute", session, "rollback", nil)
   253  	require.NoError(t, err)
   254  	wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}
   255  	utils.MustMatch(t, wantSession, session.Session, "session")
   256  	if rollbackCount := sbclookup.RollbackCount.Get(); rollbackCount != 1 {
   257  		t.Errorf("want 1, got %d", rollbackCount)
   258  	}
   259  	_ = testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0)
   260  	_ = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1)
   261  	logStats = testQueryLog(t, logChan, "TestExecute", "ROLLBACK", "rollback", 1)
   262  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   263  }
   264  
   265  func TestExecutorTransactionsAutoCommitStreaming(t *testing.T) {
   266  	executor, _, _, sbclookup := createExecutorEnv()
   267  	oltpOptions := &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLTP}
   268  	session := NewSafeSession(&vtgatepb.Session{
   269  		TargetString: "@primary",
   270  		Autocommit:   true,
   271  		Options:      oltpOptions,
   272  		SessionUUID:  "suuid",
   273  	})
   274  
   275  	logChan := QueryLogger.Subscribe("Test")
   276  	defer QueryLogger.Unsubscribe(logChan)
   277  
   278  	var results []*sqltypes.Result
   279  
   280  	// begin.
   281  	err := executor.StreamExecute(ctx, "TestExecute", session, "begin", nil, func(result *sqltypes.Result) error {
   282  		results = append(results, result)
   283  		return nil
   284  	})
   285  
   286  	require.EqualValues(t, 1, len(results), "should get empty result from begin")
   287  	assert.Empty(t, results[0].Rows, "should get empty result from begin")
   288  
   289  	require.NoError(t, err)
   290  	wantSession := &vtgatepb.Session{
   291  		InTransaction: true,
   292  		TargetString:  "@primary",
   293  		Autocommit:    true,
   294  		Options:       oltpOptions,
   295  		SessionUUID:   "suuid",
   296  	}
   297  	utils.MustMatch(t, wantSession, session.Session, "session")
   298  	assert.Zero(t, sbclookup.CommitCount.Get())
   299  	logStats := testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0)
   300  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   301  
   302  	// commit.
   303  	_, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil)
   304  	require.NoError(t, err)
   305  	_, err = executor.Execute(ctx, "TestExecute", session, "commit", nil)
   306  	require.NoError(t, err)
   307  	wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, Options: oltpOptions, SessionUUID: "suuid"}
   308  	utils.MustMatch(t, wantSession, session.Session, "session")
   309  	assert.EqualValues(t, 1, sbclookup.CommitCount.Get())
   310  
   311  	logStats = testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1)
   312  	assert.EqualValues(t, 0, logStats.CommitTime)
   313  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   314  	logStats = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1)
   315  	assert.NotEqual(t, 0, logStats.CommitTime)
   316  	assert.EqualValues(t, "suuid", logStats.SessionUUID, "logstats: expected non-empty SessionUUID")
   317  
   318  	// rollback.
   319  	_, err = executor.Execute(ctx, "TestExecute", session, "begin", nil)
   320  	require.NoError(t, err)
   321  	_, err = executor.Execute(ctx, "TestExecute", session, "select id from main1", nil)
   322  	require.NoError(t, err)
   323  	_, err = executor.Execute(ctx, "TestExecute", session, "rollback", nil)
   324  	require.NoError(t, err)
   325  	wantSession = &vtgatepb.Session{TargetString: "@primary", Autocommit: true, Options: oltpOptions, SessionUUID: "suuid"}
   326  	utils.MustMatch(t, wantSession, session.Session, "session")
   327  	assert.EqualValues(t, 1, sbclookup.RollbackCount.Get())
   328  }
   329  
   330  func TestExecutorDeleteMetadata(t *testing.T) {
   331  	vschemaacl.AuthorizedDDLUsers = "%"
   332  	defer func() {
   333  		vschemaacl.AuthorizedDDLUsers = ""
   334  	}()
   335  
   336  	executor, _, _, _ := createExecutorEnv()
   337  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true})
   338  
   339  	set := "set @@vitess_metadata.app_v1= '1'"
   340  	_, err := executor.Execute(ctx, "TestExecute", session, set, nil)
   341  	assert.NoError(t, err, "%s error: %v", set, err)
   342  
   343  	show := `show vitess_metadata variables like 'app\\_%'`
   344  	result, _ := executor.Execute(ctx, "TestExecute", session, show, nil)
   345  	assert.Len(t, result.Rows, 1)
   346  
   347  	// Fails if deleting key that doesn't exist
   348  	delQuery := "set @@vitess_metadata.doesn't_exist=''"
   349  	_, err = executor.Execute(ctx, "TestExecute", session, delQuery, nil)
   350  	assert.True(t, topo.IsErrType(err, topo.NoNode))
   351  
   352  	// Delete existing key, show should fail given the node doesn't exist
   353  	delQuery = "set @@vitess_metadata.app_v1=''"
   354  	_, err = executor.Execute(ctx, "TestExecute", session, delQuery, nil)
   355  	assert.NoError(t, err)
   356  
   357  	show = `show vitess_metadata variables like 'app\\_%'`
   358  	_, err = executor.Execute(ctx, "TestExecute", session, show, nil)
   359  	assert.True(t, topo.IsErrType(err, topo.NoNode))
   360  }
   361  
   362  func TestExecutorAutocommit(t *testing.T) {
   363  	executor, _, _, sbclookup := createExecutorEnv()
   364  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"})
   365  
   366  	logChan := QueryLogger.Subscribe("Test")
   367  	defer QueryLogger.Unsubscribe(logChan)
   368  
   369  	// autocommit = 0
   370  	startCount := sbclookup.CommitCount.Get()
   371  	_, err := executor.Execute(ctx, "TestExecute", session, "select id from main1", nil)
   372  	require.NoError(t, err)
   373  	wantSession := &vtgatepb.Session{TargetString: "@primary", InTransaction: true, FoundRows: 1, RowCount: -1}
   374  	testSession := proto.Clone(session.Session).(*vtgatepb.Session)
   375  	testSession.ShardSessions = nil
   376  	utils.MustMatch(t, wantSession, testSession, "session does not match for autocommit=0")
   377  
   378  	logStats := testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from main1", 1)
   379  	if logStats.CommitTime != 0 {
   380  		t.Errorf("logstats: expected zero CommitTime")
   381  	}
   382  	if logStats.RowsReturned == 0 {
   383  		t.Errorf("logstats: expected non-zero RowsReturned")
   384  	}
   385  
   386  	// autocommit = 1
   387  	_, err = executor.Execute(ctx, "TestExecute", session, "set autocommit=1", nil)
   388  	require.NoError(t, err)
   389  	_ = testQueryLog(t, logChan, "TestExecute", "SET", "set @@autocommit = 1", 0)
   390  
   391  	// Setting autocommit=1 commits existing transaction.
   392  	if got, want := sbclookup.CommitCount.Get(), startCount+1; got != want {
   393  		t.Errorf("Commit count: %d, want %d", got, want)
   394  	}
   395  
   396  	_, err = executor.Execute(ctx, "TestExecute", session, "update main1 set id=1", nil)
   397  	require.NoError(t, err)
   398  	wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@primary", FoundRows: 0, RowCount: 1}
   399  	utils.MustMatch(t, wantSession, session.Session, "session does not match for autocommit=1")
   400  
   401  	logStats = testQueryLog(t, logChan, "TestExecute", "UPDATE", "update main1 set id=1", 1)
   402  	assert.NotZero(t, logStats.CommitTime, "logstats: expected non-zero CommitTime")
   403  	assert.NotEqual(t, uint64(0), logStats.RowsAffected, "logstats: expected non-zero RowsAffected")
   404  
   405  	// autocommit = 1, "begin"
   406  	session.ResetTx()
   407  	startCount = sbclookup.CommitCount.Get()
   408  	_, err = executor.Execute(ctx, "TestExecute", session, "begin", nil)
   409  	require.NoError(t, err)
   410  	_ = testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0)
   411  
   412  	_, err = executor.Execute(ctx, "TestExecute", session, "update main1 set id=1", nil)
   413  	require.NoError(t, err)
   414  	wantSession = &vtgatepb.Session{InTransaction: true, Autocommit: true, TargetString: "@primary", FoundRows: 0, RowCount: 1}
   415  	testSession = proto.Clone(session.Session).(*vtgatepb.Session)
   416  	testSession.ShardSessions = nil
   417  	utils.MustMatch(t, wantSession, testSession, "session does not match for autocommit=1")
   418  	if got, want := sbclookup.CommitCount.Get(), startCount; got != want {
   419  		t.Errorf("Commit count: %d, want %d", got, want)
   420  	}
   421  
   422  	logStats = testQueryLog(t, logChan, "TestExecute", "UPDATE", "update main1 set id=1", 1)
   423  	if logStats.CommitTime != 0 {
   424  		t.Errorf("logstats: expected zero CommitTime")
   425  	}
   426  	if logStats.RowsAffected == 0 {
   427  		t.Errorf("logstats: expected non-zero RowsAffected")
   428  	}
   429  
   430  	_, err = executor.Execute(ctx, "TestExecute", session, "commit", nil)
   431  	require.NoError(t, err)
   432  	wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@primary"}
   433  	if !proto.Equal(session.Session, wantSession) {
   434  		t.Errorf("autocommit=1: %v, want %v", session.Session, wantSession)
   435  	}
   436  	if got, want := sbclookup.CommitCount.Get(), startCount+1; got != want {
   437  		t.Errorf("Commit count: %d, want %d", got, want)
   438  	}
   439  	_ = testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 1)
   440  
   441  	// transition autocommit from 0 to 1 in the middle of a transaction.
   442  	startCount = sbclookup.CommitCount.Get()
   443  	session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary"})
   444  	_, err = executor.Execute(ctx, "TestExecute", session, "begin", nil)
   445  	require.NoError(t, err)
   446  	_, err = executor.Execute(ctx, "TestExecute", session, "update main1 set id=1", nil)
   447  	require.NoError(t, err)
   448  	if got, want := sbclookup.CommitCount.Get(), startCount; got != want {
   449  		t.Errorf("Commit count: %d, want %d", got, want)
   450  	}
   451  	_, err = executor.Execute(ctx, "TestExecute", session, "set autocommit=1", nil)
   452  	require.NoError(t, err)
   453  	wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@primary"}
   454  	if !proto.Equal(session.Session, wantSession) {
   455  		t.Errorf("autocommit=1: %v, want %v", session.Session, wantSession)
   456  	}
   457  	if got, want := sbclookup.CommitCount.Get(), startCount+1; got != want {
   458  		t.Errorf("Commit count: %d, want %d", got, want)
   459  	}
   460  }
   461  
   462  func TestExecutorShowColumns(t *testing.T) {
   463  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
   464  	session := NewSafeSession(&vtgatepb.Session{TargetString: ""})
   465  
   466  	queries := []string{
   467  		"SHOW COLUMNS FROM `user` in `TestExecutor`",
   468  		"show columns from `user` in `TestExecutor`",
   469  		"ShOw CoLuMnS fRoM `user` iN `TestExecutor`",
   470  		"SHOW columns FROM `user` in `TestExecutor`",
   471  	}
   472  	for _, query := range queries {
   473  		t.Run(query, func(t *testing.T) {
   474  			_, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   475  			require.NoError(t, err)
   476  
   477  			wantQueries := []*querypb.BoundQuery{{
   478  				Sql:           "show columns from `user`",
   479  				BindVariables: map[string]*querypb.BindVariable{},
   480  			}}
   481  
   482  			assert.Equal(t, wantQueries, sbc1.Queries, "sbc1.Queries")
   483  			assert.Empty(t, sbc1.BatchQueries, "sbc1.BatchQueries")
   484  			assert.Empty(t, sbc2.Queries, "sbc2.Queries")
   485  			assert.Empty(t, sbc2.BatchQueries, "sbc2.BatchQueries")
   486  			assert.Empty(t, sbclookup.Queries, "sbclookup.Queries")
   487  			assert.Empty(t, sbclookup.BatchQueries, "sbclookup.BatchQueries")
   488  
   489  			sbc1.Queries = nil
   490  			sbc2.Queries = nil
   491  			sbclookup.Queries = nil
   492  			sbc1.BatchQueries = nil
   493  			sbc2.BatchQueries = nil
   494  			sbclookup.BatchQueries = nil
   495  		})
   496  	}
   497  
   498  }
   499  
   500  func sortString(w string) string {
   501  	s := strings.Split(w, "")
   502  	sort.Strings(s)
   503  	return strings.Join(s, "")
   504  }
   505  
   506  func assertMatchesNoOrder(t *testing.T, expected, got string) {
   507  	t.Helper()
   508  	if sortString(expected) != sortString(got) {
   509  		t.Errorf("for query: expected \n%s \nbut actual \n%s", expected, got)
   510  	}
   511  }
   512  
   513  func TestExecutorShow(t *testing.T) {
   514  	executor, _, _, sbclookup := createExecutorEnv()
   515  	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
   516  
   517  	for _, query := range []string{"show vitess_keyspaces", "show keyspaces"} {
   518  		qr, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   519  		require.NoError(t, err)
   520  		assertMatchesNoOrder(t, `[[VARCHAR("TestUnsharded")] [VARCHAR("TestMultiCol")] [VARCHAR("TestXBadVSchema")] [VARCHAR("TestXBadSharding")] [VARCHAR("TestExecutor")]]`, fmt.Sprintf("%v", qr.Rows))
   521  	}
   522  
   523  	for _, query := range []string{"show databases", "show DATABASES", "show schemas", "show SCHEMAS"} {
   524  		qr, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   525  		require.NoError(t, err)
   526  		// Showing default tables (5+4[default])
   527  		assertMatchesNoOrder(t, `[[VARCHAR("TestUnsharded")] [VARCHAR("TestMultiCol")] [VARCHAR("TestXBadVSchema")] [VARCHAR("TestXBadSharding")] [VARCHAR("TestExecutor")]] [VARCHAR("information_schema")] [VARCHAR("mysql")] [VARCHAR("sys")] [VARCHAR("performance_schema")]`, fmt.Sprintf("%v", qr.Rows))
   528  	}
   529  
   530  	_, err := executor.Execute(ctx, "TestExecute", session, "show variables", nil)
   531  	require.NoError(t, err)
   532  	_, err = executor.Execute(ctx, "TestExecute", session, "show collation", nil)
   533  	require.NoError(t, err)
   534  	_, err = executor.Execute(ctx, "TestExecute", session, "show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'", nil)
   535  	require.NoError(t, err)
   536  
   537  	_, err = executor.Execute(ctx, "TestExecute", session, "use @primary", nil)
   538  	require.NoError(t, err)
   539  	_, err = executor.Execute(ctx, "TestExecute", session, "show tables", nil)
   540  	assert.EqualError(t, err, errNoKeyspace.Error(), "'show tables' should fail without a keyspace")
   541  	assert.Empty(t, sbclookup.Queries, "sbclookup unexpectedly has queries already")
   542  
   543  	showResults := &sqltypes.Result{
   544  		Fields: []*querypb.Field{
   545  			{Name: "Tables_in_keyspace", Type: sqltypes.VarChar},
   546  		},
   547  		RowsAffected: 1,
   548  		InsertID:     0,
   549  		Rows: [][]sqltypes.Value{{
   550  			sqltypes.NewVarChar("some_table"),
   551  		}},
   552  	}
   553  	sbclookup.SetResults([]*sqltypes.Result{showResults})
   554  
   555  	query := fmt.Sprintf("show tables from %v", KsTestUnsharded)
   556  	qr, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   557  	require.NoError(t, err)
   558  
   559  	assert.Equal(t, 1, len(sbclookup.Queries), "Tablet should have received one 'show' query. Instead received: %v", sbclookup.Queries)
   560  	lastQuery := sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   561  	want := "show tables"
   562  	assert.Equal(t, want, lastQuery, "Got: %v, want %v", lastQuery, want)
   563  
   564  	wantqr := showResults
   565  	utils.MustMatch(t, wantqr, qr, fmt.Sprintf("unexpected results running query: %s", query))
   566  
   567  	wantErrNoTable := "table unknown_table not found"
   568  	_, err = executor.Execute(ctx, "TestExecute", session, "show create table unknown_table", nil)
   569  	assert.EqualErrorf(t, err, wantErrNoTable, "Got: %v. Want: %v", wantErrNoTable)
   570  
   571  	// SHOW CREATE table using vschema to find keyspace.
   572  	_, err = executor.Execute(ctx, "TestExecute", session, "show create table user_seq", nil)
   573  	require.NoError(t, err)
   574  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   575  	wantQuery := "show create table user_seq"
   576  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   577  
   578  	// SHOW CREATE table with query-provided keyspace
   579  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show create table %v.unknown", KsTestUnsharded), nil)
   580  	require.NoError(t, err)
   581  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   582  	wantQuery = "show create table unknown"
   583  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   584  
   585  	// SHOW KEYS with two different syntax
   586  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show keys from %v.unknown", KsTestUnsharded), nil)
   587  	require.NoError(t, err)
   588  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   589  	wantQuery = "show indexes from unknown"
   590  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   591  
   592  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show keys from unknown from %v", KsTestUnsharded), nil)
   593  	require.NoError(t, err)
   594  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   595  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   596  
   597  	// SHOW INDEX with two different syntax
   598  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show index from %v.unknown", KsTestUnsharded), nil)
   599  	require.NoError(t, err)
   600  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   601  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   602  
   603  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show index from unknown from %v", KsTestUnsharded), nil)
   604  	require.NoError(t, err)
   605  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   606  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   607  
   608  	// SHOW INDEXES with two different syntax
   609  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show indexes from %v.unknown", KsTestUnsharded), nil)
   610  	require.NoError(t, err)
   611  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   612  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   613  
   614  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show indexes from unknown from %v", KsTestUnsharded), nil)
   615  	require.NoError(t, err)
   616  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   617  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   618  
   619  	// SHOW EXTENDED {INDEX | INDEXES | KEYS}
   620  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show extended index from unknown from %v", KsTestUnsharded), nil)
   621  	require.NoError(t, err)
   622  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   623  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   624  
   625  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show extended indexes from unknown from %v", KsTestUnsharded), nil)
   626  	require.NoError(t, err)
   627  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   628  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   629  
   630  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show extended keys from unknown from %v", KsTestUnsharded), nil)
   631  	require.NoError(t, err)
   632  	lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql
   633  	assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery)
   634  
   635  	// Set desitation keyspace in session
   636  	session.TargetString = KsTestUnsharded
   637  	_, err = executor.Execute(ctx, "TestExecute", session, "show create table unknown", nil)
   638  	require.NoError(t, err)
   639  
   640  	_, err = executor.Execute(ctx, "TestExecute", session, "show full columns from table1", nil)
   641  	require.NoError(t, err)
   642  
   643  	// Reset target string so other tests dont fail.
   644  	session.TargetString = "@primary"
   645  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show full columns from unknown from %v", KsTestUnsharded), nil)
   646  	require.NoError(t, err)
   647  
   648  	for _, query := range []string{"show charset", "show character set"} {
   649  		qr, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   650  		require.NoError(t, err)
   651  		wantqr := &sqltypes.Result{
   652  			Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}),
   653  			Rows: [][]sqltypes.Value{
   654  				append(buildVarCharRow(
   655  					"utf8",
   656  					"UTF-8 Unicode",
   657  					"utf8_general_ci"), sqltypes.NewInt32(3)),
   658  				append(buildVarCharRow(
   659  					"utf8mb4",
   660  					"UTF-8 Unicode",
   661  					"utf8mb4_general_ci"),
   662  					sqltypes.NewInt32(4)),
   663  			},
   664  		}
   665  
   666  		utils.MustMatch(t, wantqr, qr, query)
   667  	}
   668  
   669  	for _, query := range []string{"show charset like '%foo'", "show character set like 'foo%'", "show charset like 'foo%'", "show character set where foo like 'utf8'", "show character set where charset like '%foo'", "show charset where charset = '%foo'"} {
   670  		qr, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   671  		require.NoError(t, err)
   672  		wantqr := &sqltypes.Result{
   673  			Fields:       append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}),
   674  			Rows:         [][]sqltypes.Value{},
   675  			RowsAffected: 0,
   676  		}
   677  
   678  		utils.MustMatch(t, wantqr, qr, query)
   679  	}
   680  
   681  	for _, query := range []string{"show charset like 'utf8'", "show character set like 'utf8'", "show charset where charset = 'utf8'", "show character set where charset = 'utf8'"} {
   682  		qr, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   683  		require.NoError(t, err)
   684  		wantqr := &sqltypes.Result{
   685  			Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}),
   686  			Rows: [][]sqltypes.Value{
   687  				append(buildVarCharRow(
   688  					"utf8",
   689  					"UTF-8 Unicode",
   690  					"utf8_general_ci"), sqltypes.NewInt32(3)),
   691  			},
   692  		}
   693  
   694  		utils.MustMatch(t, wantqr, qr, query)
   695  	}
   696  
   697  	for _, query := range []string{"show charset like 'utf8mb4'", "show character set like 'utf8mb4'", "show charset where charset = 'utf8mb4'", "show character set where charset = 'utf8mb4'"} {
   698  		qr, err := executor.Execute(ctx, "TestExecute", session, query, nil)
   699  		require.NoError(t, err)
   700  		wantqr := &sqltypes.Result{
   701  			Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}),
   702  			Rows: [][]sqltypes.Value{
   703  				append(buildVarCharRow(
   704  					"utf8mb4",
   705  					"UTF-8 Unicode",
   706  					"utf8mb4_general_ci"),
   707  					sqltypes.NewInt32(4)),
   708  			},
   709  		}
   710  		utils.MustMatch(t, wantqr, qr, query)
   711  	}
   712  
   713  	query = "show engines"
   714  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   715  	require.NoError(t, err)
   716  	wantqr = &sqltypes.Result{
   717  		Fields: buildVarCharFields("Engine", "Support", "Comment", "Transactions", "XA", "Savepoints"),
   718  		Rows: [][]sqltypes.Value{
   719  			buildVarCharRow(
   720  				"InnoDB",
   721  				"DEFAULT",
   722  				"Supports transactions, row-level locking, and foreign keys",
   723  				"YES",
   724  				"YES",
   725  				"YES"),
   726  		},
   727  	}
   728  	utils.MustMatch(t, wantqr, qr, query)
   729  
   730  	query = "show plugins"
   731  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   732  	require.NoError(t, err)
   733  	wantqr = &sqltypes.Result{
   734  		Fields: buildVarCharFields("Name", "Status", "Type", "Library", "License"),
   735  		Rows: [][]sqltypes.Value{
   736  			buildVarCharRow(
   737  				"InnoDB",
   738  				"ACTIVE",
   739  				"STORAGE ENGINE",
   740  				"NULL",
   741  				"GPL"),
   742  		},
   743  	}
   744  	utils.MustMatch(t, wantqr, qr, query)
   745  
   746  	for _, sql := range []string{"show session status", "show session status like 'Ssl_cipher'"} {
   747  		qr, err = executor.Execute(ctx, "TestExecute", session, sql, nil)
   748  		require.NoError(t, err)
   749  		wantqr = &sqltypes.Result{
   750  			Fields: []*querypb.Field{
   751  				{Name: "id", Type: sqltypes.Int32},
   752  				{Name: "value", Type: sqltypes.VarChar},
   753  			},
   754  			Rows: [][]sqltypes.Value{
   755  				{sqltypes.NewInt32(1), sqltypes.NewVarChar("foo")},
   756  			},
   757  		}
   758  
   759  		utils.MustMatch(t, wantqr, qr, sql)
   760  	}
   761  
   762  	// Test SHOW FULL COLUMNS FROM where query has a qualifier
   763  	_, err = executor.Execute(ctx, "TestExecute", session, fmt.Sprintf("show full columns from %v.table1", KsTestUnsharded), nil)
   764  	require.NoError(t, err)
   765  
   766  	query = "show vitess_shards"
   767  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   768  	require.NoError(t, err)
   769  
   770  	// Just test for first & last.
   771  	qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]}
   772  	wantqr = &sqltypes.Result{
   773  		Fields: buildVarCharFields("Shards"),
   774  		Rows: [][]sqltypes.Value{
   775  			buildVarCharRow("TestExecutor/-20"),
   776  			buildVarCharRow("TestXBadVSchema/e0-"),
   777  		},
   778  	}
   779  	utils.MustMatch(t, wantqr, qr, query)
   780  
   781  	query = "show vitess_shards like 'TestExecutor/%'"
   782  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   783  	require.NoError(t, err)
   784  
   785  	// Just test for first & last.
   786  	qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]}
   787  	wantqr = &sqltypes.Result{
   788  		Fields: buildVarCharFields("Shards"),
   789  		Rows: [][]sqltypes.Value{
   790  			buildVarCharRow("TestExecutor/-20"),
   791  			buildVarCharRow("TestExecutor/e0-"),
   792  		},
   793  	}
   794  	utils.MustMatch(t, wantqr, qr, query)
   795  
   796  	query = "show vitess_shards like 'TestExec%/%'"
   797  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   798  	require.NoError(t, err)
   799  
   800  	// Just test for first & last.
   801  	qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]}
   802  	wantqr = &sqltypes.Result{
   803  		Fields: buildVarCharFields("Shards"),
   804  		Rows: [][]sqltypes.Value{
   805  			buildVarCharRow("TestExecutor/-20"),
   806  			buildVarCharRow("TestExecutor/e0-"),
   807  		},
   808  	}
   809  	utils.MustMatch(t, wantqr, qr, query)
   810  
   811  	query = "show vitess_replication_status"
   812  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   813  	require.NoError(t, err)
   814  	qr.Rows = [][]sqltypes.Value{}
   815  	wantqr = &sqltypes.Result{
   816  		Fields: buildVarCharFields("Keyspace", "Shard", "TabletType", "Alias", "Hostname", "ReplicationSource", "ReplicationHealth", "ReplicationLag", "ThrottlerStatus"),
   817  		Rows:   [][]sqltypes.Value{},
   818  	}
   819  	utils.MustMatch(t, wantqr, qr, query)
   820  	query = "show vitess_replication_status like 'x'"
   821  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   822  	require.NoError(t, err)
   823  	qr.Rows = [][]sqltypes.Value{}
   824  	wantqr = &sqltypes.Result{
   825  		Fields: buildVarCharFields("Keyspace", "Shard", "TabletType", "Alias", "Hostname", "ReplicationSource", "ReplicationHealth", "ReplicationLag", "ThrottlerStatus"),
   826  		Rows:   [][]sqltypes.Value{},
   827  	}
   828  	utils.MustMatch(t, wantqr, qr, query)
   829  
   830  	query = "show vitess_tablets"
   831  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   832  	require.NoError(t, err)
   833  	// Just test for first & last.
   834  	qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]}
   835  	wantqr = &sqltypes.Result{
   836  		Fields: buildVarCharFields("Cell", "Keyspace", "Shard", "TabletType", "State", "Alias", "Hostname", "PrimaryTermStartTime"),
   837  		Rows: [][]sqltypes.Value{
   838  			buildVarCharRow("aa", "TestExecutor", "-20", "PRIMARY", "SERVING", "aa-0000000001", "-20", "1970-01-01T00:00:01Z"),
   839  			buildVarCharRow("aa", "TestXBadVSchema", "-20", "PRIMARY", "SERVING", "aa-0000000009", "random", "1970-01-01T00:00:01Z"),
   840  		},
   841  	}
   842  	utils.MustMatch(t, wantqr, qr, query)
   843  
   844  	query = "show vitess_tablets like 'x'"
   845  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   846  	require.NoError(t, err)
   847  	wantqr = &sqltypes.Result{
   848  		Fields: buildVarCharFields("Cell", "Keyspace", "Shard", "TabletType", "State", "Alias", "Hostname", "PrimaryTermStartTime"),
   849  		Rows:   [][]sqltypes.Value{},
   850  	}
   851  	utils.MustMatch(t, wantqr, qr, fmt.Sprintf("%q should be empty", query))
   852  
   853  	query = "show vitess_tablets like '-20%'"
   854  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   855  	require.NoError(t, err)
   856  	wantqr = &sqltypes.Result{
   857  		Fields: buildVarCharFields("Cell", "Keyspace", "Shard", "TabletType", "State", "Alias", "Hostname", "PrimaryTermStartTime"),
   858  		Rows: [][]sqltypes.Value{
   859  			buildVarCharRow("aa", "TestExecutor", "-20", "PRIMARY", "SERVING", "aa-0000000001", "-20", "1970-01-01T00:00:01Z"),
   860  		},
   861  	}
   862  	utils.MustMatch(t, wantqr, qr, query)
   863  
   864  	query = "show vschema vindexes"
   865  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   866  	require.NoError(t, err)
   867  	wantqr = &sqltypes.Result{
   868  		Fields: buildVarCharFields("Keyspace", "Name", "Type", "Params", "Owner"),
   869  		Rows: [][]sqltypes.Value{
   870  			buildVarCharRow("TestExecutor", "cfc", "cfc", "", ""),
   871  			buildVarCharRow("TestExecutor", "hash_index", "hash", "", ""),
   872  			buildVarCharRow("TestExecutor", "idx1", "hash", "", ""),
   873  			buildVarCharRow("TestExecutor", "idx_noauto", "hash", "", "noauto_table"),
   874  			buildVarCharRow("TestExecutor", "insert_ignore_idx", "lookup_hash", "from=fromcol; table=ins_lookup; to=tocol", "insert_ignore_test"),
   875  			buildVarCharRow("TestExecutor", "keyspace_id", "numeric", "", ""),
   876  			buildVarCharRow("TestExecutor", "krcol_unique_vdx", "keyrange_lookuper_unique", "", ""),
   877  			buildVarCharRow("TestExecutor", "krcol_vdx", "keyrange_lookuper", "", ""),
   878  			buildVarCharRow("TestExecutor", "music_user_map", "lookup_hash_unique", "from=music_id; table=music_user_map; to=user_id", "music"),
   879  			buildVarCharRow("TestExecutor", "name_lastname_keyspace_id_map", "lookup", "from=name,lastname; table=name_lastname_keyspace_id_map; to=keyspace_id", "user2"),
   880  			buildVarCharRow("TestExecutor", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"),
   881  			buildVarCharRow("TestExecutor", "regional_vdx", "region_experimental", "region_bytes=1", ""),
   882  			buildVarCharRow("TestExecutor", "t1_lkp_vdx", "consistent_lookup_unique", "from=unq_col; table=t1_lkp_idx; to=keyspace_id", "t1"),
   883  			buildVarCharRow("TestExecutor", "t2_erl_lu_vdx", "lookup_unique", "from=erl_lu_col; read_lock=exclusive; table=TestUnsharded.erl_lu_idx; to=keyspace_id", "t2_lookup"),
   884  			buildVarCharRow("TestExecutor", "t2_lu_vdx", "lookup_hash_unique", "from=lu_col; table=TestUnsharded.lu_idx; to=keyspace_id", "t2_lookup"),
   885  			buildVarCharRow("TestExecutor", "t2_nrl_lu_vdx", "lookup_unique", "from=nrl_lu_col; read_lock=none; table=TestUnsharded.nrl_lu_idx; to=keyspace_id", "t2_lookup"),
   886  			buildVarCharRow("TestExecutor", "t2_nv_lu_vdx", "lookup_unique", "from=nv_lu_col; no_verify=true; table=TestUnsharded.nv_lu_idx; to=keyspace_id", "t2_lookup"),
   887  			buildVarCharRow("TestExecutor", "t2_srl_lu_vdx", "lookup_unique", "from=srl_lu_col; read_lock=shared; table=TestUnsharded.srl_lu_idx; to=keyspace_id", "t2_lookup"),
   888  			buildVarCharRow("TestExecutor", "t2_wo_lu_vdx", "lookup_unique", "from=wo_lu_col; table=TestUnsharded.wo_lu_idx; to=keyspace_id; write_only=true", "t2_lookup"),
   889  			buildVarCharRow("TestMultiCol", "multicol_vdx", "multicol", "column_bytes=1,3,4; column_count=3; column_vindex=hash,binary,unicode_loose_xxhash", ""),
   890  		},
   891  	}
   892  	utils.MustMatch(t, wantqr, qr, query)
   893  
   894  	query = "show vschema vindexes on TestExecutor.user"
   895  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   896  	require.NoError(t, err)
   897  	wantqr = &sqltypes.Result{
   898  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   899  		Rows: [][]sqltypes.Value{
   900  			buildVarCharRow("Id", "hash_index", "hash", "", ""),
   901  			buildVarCharRow("name", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"),
   902  		},
   903  	}
   904  	utils.MustMatch(t, wantqr, qr, query)
   905  
   906  	query = "show vschema vindexes on user"
   907  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   908  	wantErr := errNoKeyspace.Error()
   909  	assert.EqualError(t, err, wantErr, query)
   910  
   911  	query = "show vschema vindexes on TestExecutor.garbage"
   912  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   913  	wantErr = "VT05005: table 'garbage' does not exist in keyspace 'TestExecutor'"
   914  	assert.EqualError(t, err, wantErr, query)
   915  
   916  	query = "show vschema vindexes on user"
   917  	session.TargetString = "TestExecutor"
   918  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   919  	require.NoError(t, err)
   920  	wantqr = &sqltypes.Result{
   921  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   922  		Rows: [][]sqltypes.Value{
   923  			buildVarCharRow("Id", "hash_index", "hash", "", ""),
   924  			buildVarCharRow("name", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"),
   925  		},
   926  	}
   927  	utils.MustMatch(t, wantqr, qr, query)
   928  
   929  	query = "show vschema vindexes on user2"
   930  	session.TargetString = "TestExecutor"
   931  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   932  	require.NoError(t, err)
   933  	wantqr = &sqltypes.Result{
   934  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   935  		Rows: [][]sqltypes.Value{
   936  			buildVarCharRow("id", "hash_index", "hash", "", ""),
   937  			buildVarCharRow("name, lastname", "name_lastname_keyspace_id_map", "lookup", "from=name,lastname; table=name_lastname_keyspace_id_map; to=keyspace_id", "user2"),
   938  		},
   939  	}
   940  	utils.MustMatch(t, wantqr, qr, query)
   941  
   942  	query = "show vschema vindexes on garbage"
   943  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   944  	wantErr = "VT05005: table 'garbage' does not exist in keyspace 'TestExecutor'"
   945  	assert.EqualError(t, err, wantErr, query)
   946  
   947  	query = "show warnings"
   948  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   949  	require.NoError(t, err)
   950  	wantqr = &sqltypes.Result{
   951  		Fields: []*querypb.Field{
   952  			{Name: "Level", Type: sqltypes.VarChar},
   953  			{Name: "Code", Type: sqltypes.Uint16},
   954  			{Name: "Message", Type: sqltypes.VarChar},
   955  		},
   956  		Rows: [][]sqltypes.Value{},
   957  	}
   958  	utils.MustMatch(t, wantqr, qr, query)
   959  
   960  	query = "show warnings"
   961  	session.Warnings = []*querypb.QueryWarning{}
   962  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   963  	require.NoError(t, err)
   964  	wantqr = &sqltypes.Result{
   965  		Fields: []*querypb.Field{
   966  			{Name: "Level", Type: sqltypes.VarChar},
   967  			{Name: "Code", Type: sqltypes.Uint16},
   968  			{Name: "Message", Type: sqltypes.VarChar},
   969  		},
   970  		Rows: [][]sqltypes.Value{},
   971  	}
   972  	utils.MustMatch(t, wantqr, qr, query)
   973  
   974  	query = "show warnings"
   975  	session.Warnings = []*querypb.QueryWarning{
   976  		{Code: mysql.ERBadTable, Message: "bad table"},
   977  		{Code: mysql.EROutOfResources, Message: "ks/-40: query timed out"},
   978  	}
   979  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   980  	require.NoError(t, err)
   981  	wantqr = &sqltypes.Result{
   982  		Fields: []*querypb.Field{
   983  			{Name: "Level", Type: sqltypes.VarChar},
   984  			{Name: "Code", Type: sqltypes.Uint16},
   985  			{Name: "Message", Type: sqltypes.VarChar},
   986  		},
   987  
   988  		Rows: [][]sqltypes.Value{
   989  			{sqltypes.NewVarChar("Warning"), sqltypes.NewUint32(mysql.ERBadTable), sqltypes.NewVarChar("bad table")},
   990  			{sqltypes.NewVarChar("Warning"), sqltypes.NewUint32(mysql.EROutOfResources), sqltypes.NewVarChar("ks/-40: query timed out")},
   991  		},
   992  	}
   993  	utils.MustMatch(t, wantqr, qr, query)
   994  
   995  	// Make sure it still works when one of the keyspaces is in a bad state
   996  	getSandbox("TestExecutor").SrvKeyspaceMustFail++
   997  	query = "show vitess_shards"
   998  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
   999  	require.NoError(t, err)
  1000  	// Just test for first & last.
  1001  	qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]}
  1002  	wantqr = &sqltypes.Result{
  1003  		Fields: buildVarCharFields("Shards"),
  1004  		Rows: [][]sqltypes.Value{
  1005  			buildVarCharRow("TestMultiCol/-20"),
  1006  			buildVarCharRow("TestXBadVSchema/e0-"),
  1007  		},
  1008  	}
  1009  	utils.MustMatch(t, wantqr, qr, fmt.Sprintf("%s, with a bad keyspace", query))
  1010  
  1011  	query = "show vschema tables"
  1012  	session = NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded})
  1013  	qr, err = executor.Execute(ctx, "TestExecute", session, query, nil)
  1014  	require.NoError(t, err)
  1015  	wantqr = &sqltypes.Result{
  1016  		Fields: buildVarCharFields("Tables"),
  1017  		Rows: [][]sqltypes.Value{
  1018  			buildVarCharRow("dual"),
  1019  			buildVarCharRow("erl_lu_idx"),
  1020  			buildVarCharRow("ins_lookup"),
  1021  			buildVarCharRow("lu_idx"),
  1022  			buildVarCharRow("main1"),
  1023  			buildVarCharRow("music_user_map"),
  1024  			buildVarCharRow("name_lastname_keyspace_id_map"),
  1025  			buildVarCharRow("name_user_map"),
  1026  			buildVarCharRow("nrl_lu_idx"),
  1027  			buildVarCharRow("nv_lu_idx"),
  1028  			buildVarCharRow("simple"),
  1029  			buildVarCharRow("srl_lu_idx"),
  1030  			buildVarCharRow("user_msgs"),
  1031  			buildVarCharRow("user_seq"),
  1032  			buildVarCharRow("wo_lu_idx"),
  1033  			buildVarCharRow("zip_detail"),
  1034  		},
  1035  	}
  1036  	utils.MustMatch(t, wantqr, qr, query)
  1037  
  1038  	query = "show vschema tables"
  1039  	session = NewSafeSession(&vtgatepb.Session{})
  1040  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
  1041  	want = errNoKeyspace.Error()
  1042  	assert.EqualError(t, err, want, query)
  1043  
  1044  	query = "show 10"
  1045  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
  1046  	want = "syntax error at position 8 near '10'"
  1047  	assert.EqualError(t, err, want, query)
  1048  
  1049  	query = "show vschema tables"
  1050  	session = NewSafeSession(&vtgatepb.Session{TargetString: "no_such_keyspace"})
  1051  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
  1052  	want = "VT05003: unknown database 'no_such_keyspace' in vschema"
  1053  	assert.EqualError(t, err, want, query)
  1054  
  1055  	query = "show vitess_migrations"
  1056  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
  1057  	want = "VT05003: unknown database 'no_such_keyspace' in vschema"
  1058  	assert.EqualError(t, err, want, query)
  1059  
  1060  	query = "show vitess_migrations from ks like '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90'"
  1061  	_, err = executor.Execute(ctx, "TestExecute", session, query, nil)
  1062  	want = "VT05003: unknown database 'ks' in vschema"
  1063  	assert.EqualError(t, err, want, query)
  1064  }
  1065  
  1066  func TestExecutorShowTargeted(t *testing.T) {
  1067  	executor, _, sbc2, _ := createExecutorEnv()
  1068  	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/40-60"})
  1069  
  1070  	queries := []string{
  1071  		"show databases",
  1072  		"show variables like 'read_only'",
  1073  		"show collation",
  1074  		"show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'",
  1075  		"show tables",
  1076  		fmt.Sprintf("show tables from %v", KsTestUnsharded),
  1077  		"show create table user_seq",
  1078  		"show full columns from table1",
  1079  		"show plugins",
  1080  		"show warnings",
  1081  	}
  1082  
  1083  	for _, sql := range queries {
  1084  		_, err := executor.Execute(ctx, "TestExecutorShowTargeted", session, sql, nil)
  1085  		require.NoError(t, err)
  1086  		assert.NotZero(t, len(sbc2.Queries), "Tablet should have received 'show' query")
  1087  		lastQuery := sbc2.Queries[len(sbc2.Queries)-1].Sql
  1088  		assert.Equal(t, sql, lastQuery, "Got: %v, want %v", lastQuery, sql)
  1089  	}
  1090  }
  1091  
  1092  func TestExecutorUse(t *testing.T) {
  1093  	executor, _, _, _ := createExecutorEnv()
  1094  	session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary"})
  1095  
  1096  	stmts := []string{
  1097  		"use TestExecutor",
  1098  		"use `TestExecutor:-80@primary`",
  1099  	}
  1100  	want := []string{
  1101  		"TestExecutor",
  1102  		"TestExecutor:-80@primary",
  1103  	}
  1104  	for i, stmt := range stmts {
  1105  		_, err := executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1106  		if err != nil {
  1107  			t.Error(err)
  1108  		}
  1109  		wantSession := &vtgatepb.Session{Autocommit: true, TargetString: want[i], RowCount: -1}
  1110  		utils.MustMatch(t, wantSession, session.Session, "session does not match")
  1111  	}
  1112  
  1113  	_, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use 1", nil)
  1114  	wantErr := "syntax error at position 6 near '1'"
  1115  	if err == nil || err.Error() != wantErr {
  1116  		t.Errorf("got: %v, want %v", err, wantErr)
  1117  	}
  1118  
  1119  	_, err = executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use UnexistentKeyspace", nil)
  1120  	require.EqualError(t, err, "VT05003: unknown database 'UnexistentKeyspace' in vschema")
  1121  }
  1122  
  1123  func TestExecutorComment(t *testing.T) {
  1124  	executor, _, _, _ := createExecutorEnv()
  1125  
  1126  	stmts := []string{
  1127  		"/*! SET autocommit=1*/",
  1128  		"/*!50708 SET @x=5000*/",
  1129  	}
  1130  	wantResult := &sqltypes.Result{}
  1131  
  1132  	for _, stmt := range stmts {
  1133  		gotResult, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil)
  1134  		if err != nil {
  1135  			t.Error(err)
  1136  		}
  1137  		if !gotResult.Equal(wantResult) {
  1138  			t.Errorf("Exec %s: %v, want %v", stmt, gotResult, wantResult)
  1139  		}
  1140  	}
  1141  }
  1142  
  1143  func TestExecutorOther(t *testing.T) {
  1144  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
  1145  
  1146  	type cnts struct {
  1147  		Sbc1Cnt      int64
  1148  		Sbc2Cnt      int64
  1149  		SbcLookupCnt int64
  1150  	}
  1151  
  1152  	tcs := []struct {
  1153  		targetStr string
  1154  
  1155  		hasNoKeyspaceErr       bool
  1156  		hasDestinationShardErr bool
  1157  		wantCnts               cnts
  1158  	}{
  1159  		{
  1160  			targetStr:        "",
  1161  			hasNoKeyspaceErr: true,
  1162  		},
  1163  		{
  1164  			targetStr:              "TestExecutor[-]",
  1165  			hasDestinationShardErr: true,
  1166  		},
  1167  		{
  1168  			targetStr: KsTestUnsharded,
  1169  			wantCnts: cnts{
  1170  				Sbc1Cnt:      0,
  1171  				Sbc2Cnt:      0,
  1172  				SbcLookupCnt: 1,
  1173  			},
  1174  		},
  1175  		{
  1176  			targetStr: "TestExecutor",
  1177  			wantCnts: cnts{
  1178  				Sbc1Cnt:      1,
  1179  				Sbc2Cnt:      0,
  1180  				SbcLookupCnt: 0,
  1181  			},
  1182  		},
  1183  		{
  1184  			targetStr: "TestExecutor/-20",
  1185  			wantCnts: cnts{
  1186  				Sbc1Cnt:      1,
  1187  				Sbc2Cnt:      0,
  1188  				SbcLookupCnt: 0,
  1189  			},
  1190  		},
  1191  		{
  1192  			targetStr: "TestExecutor[00]",
  1193  			wantCnts: cnts{
  1194  				Sbc1Cnt:      1,
  1195  				Sbc2Cnt:      0,
  1196  				SbcLookupCnt: 0,
  1197  			},
  1198  		},
  1199  	}
  1200  
  1201  	stmts := []string{
  1202  		"analyze table t1",
  1203  		"describe select * from t1",
  1204  		"explain select * from t1",
  1205  		"repair table t1",
  1206  		"optimize table t1",
  1207  	}
  1208  
  1209  	for _, stmt := range stmts {
  1210  		for _, tc := range tcs {
  1211  			t.Run(fmt.Sprintf("%s-%s", stmt, tc.targetStr), func(t *testing.T) {
  1212  				sbc1.ExecCount.Set(0)
  1213  				sbc2.ExecCount.Set(0)
  1214  				sbclookup.ExecCount.Set(0)
  1215  
  1216  				_, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil)
  1217  				if tc.hasNoKeyspaceErr {
  1218  					assert.Error(t, err, errNoKeyspace)
  1219  				} else if tc.hasDestinationShardErr {
  1220  					assert.Errorf(t, err, "Destination can only be a single shard for statement: %s", stmt)
  1221  				} else {
  1222  					assert.NoError(t, err)
  1223  				}
  1224  
  1225  				utils.MustMatch(t, tc.wantCnts, cnts{
  1226  					Sbc1Cnt:      sbc1.ExecCount.Get(),
  1227  					Sbc2Cnt:      sbc2.ExecCount.Get(),
  1228  					SbcLookupCnt: sbclookup.ExecCount.Get(),
  1229  				})
  1230  			})
  1231  		}
  1232  	}
  1233  }
  1234  
  1235  func TestExecutorDDL(t *testing.T) {
  1236  	logChan := QueryLogger.Subscribe("Test")
  1237  	defer QueryLogger.Unsubscribe(logChan)
  1238  
  1239  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
  1240  
  1241  	type cnts struct {
  1242  		Sbc1Cnt      int64
  1243  		Sbc2Cnt      int64
  1244  		SbcLookupCnt int64
  1245  	}
  1246  
  1247  	tcs := []struct {
  1248  		targetStr string
  1249  
  1250  		hasNoKeyspaceErr bool
  1251  		shardQueryCnt    int
  1252  		wantCnts         cnts
  1253  	}{
  1254  		{
  1255  			targetStr:        "",
  1256  			hasNoKeyspaceErr: true,
  1257  		},
  1258  		{
  1259  			targetStr:     KsTestUnsharded,
  1260  			shardQueryCnt: 1,
  1261  			wantCnts: cnts{
  1262  				Sbc1Cnt:      0,
  1263  				Sbc2Cnt:      0,
  1264  				SbcLookupCnt: 1,
  1265  			},
  1266  		},
  1267  		{
  1268  			targetStr:     "TestExecutor",
  1269  			shardQueryCnt: 8,
  1270  			wantCnts: cnts{
  1271  				Sbc1Cnt:      1,
  1272  				Sbc2Cnt:      1,
  1273  				SbcLookupCnt: 0,
  1274  			},
  1275  		},
  1276  		{
  1277  			targetStr:     "TestExecutor/-20",
  1278  			shardQueryCnt: 1,
  1279  			wantCnts: cnts{
  1280  				Sbc1Cnt:      1,
  1281  				Sbc2Cnt:      0,
  1282  				SbcLookupCnt: 0,
  1283  			},
  1284  		},
  1285  	}
  1286  
  1287  	stmts := []string{
  1288  		"create table t2(id bigint primary key)",
  1289  		"alter table t2 add primary key (id)",
  1290  		"rename table t2 to t3",
  1291  		"truncate table t2",
  1292  		"drop table t2",
  1293  		`create table test_partitioned (
  1294  			id bigint,
  1295  			date_create int,		
  1296  			primary key(id)
  1297  		) Engine=InnoDB	/*!50100 PARTITION BY RANGE (date_create)
  1298  		  (PARTITION p2018_06_14 VALUES LESS THAN (1528959600) ENGINE = InnoDB,
  1299  		   PARTITION p2018_06_15 VALUES LESS THAN (1529046000) ENGINE = InnoDB,
  1300  		   PARTITION p2018_06_16 VALUES LESS THAN (1529132400) ENGINE = InnoDB,
  1301  		   PARTITION p2018_06_17 VALUES LESS THAN (1529218800) ENGINE = InnoDB)*/`,
  1302  	}
  1303  
  1304  	for _, stmt := range stmts {
  1305  		for _, tc := range tcs {
  1306  			sbc1.ExecCount.Set(0)
  1307  			sbc2.ExecCount.Set(0)
  1308  			sbclookup.ExecCount.Set(0)
  1309  			stmtType := "DDL"
  1310  			_, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil)
  1311  			if tc.hasNoKeyspaceErr {
  1312  				require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail: %q", stmt)
  1313  				stmtType = "" // For error case, plan is not generated to query log will not contain any stmtType.
  1314  			} else {
  1315  				require.NoError(t, err, "did not expect error for query: %q", stmt)
  1316  			}
  1317  
  1318  			diff := cmp.Diff(tc.wantCnts, cnts{
  1319  				Sbc1Cnt:      sbc1.ExecCount.Get(),
  1320  				Sbc2Cnt:      sbc2.ExecCount.Get(),
  1321  				SbcLookupCnt: sbclookup.ExecCount.Get(),
  1322  			})
  1323  			if diff != "" {
  1324  				t.Errorf("stmt: %s\ntc: %+v\n-want,+got:\n%s", stmt, tc, diff)
  1325  			}
  1326  
  1327  			testQueryLog(t, logChan, "TestExecute", stmtType, stmt, tc.shardQueryCnt)
  1328  		}
  1329  	}
  1330  
  1331  	stmts2 := []struct {
  1332  		input  string
  1333  		hasErr bool
  1334  	}{
  1335  		{input: "create table t1(id bigint primary key)", hasErr: false},
  1336  		{input: "drop table t1", hasErr: false},
  1337  		{input: "drop table t2", hasErr: true},
  1338  		{input: "drop view t1", hasErr: false},
  1339  		{input: "drop view t2", hasErr: true},
  1340  		{input: "alter view t1 as select * from t1", hasErr: false},
  1341  		{input: "alter view t2 as select * from t1", hasErr: true},
  1342  	}
  1343  
  1344  	for _, stmt := range stmts2 {
  1345  		sbc1.ExecCount.Set(0)
  1346  		sbc2.ExecCount.Set(0)
  1347  		sbclookup.ExecCount.Set(0)
  1348  		_, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: ""}), stmt.input, nil)
  1349  		if stmt.hasErr {
  1350  			require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail")
  1351  			testQueryLog(t, logChan, "TestExecute", "", stmt.input, 0)
  1352  		} else {
  1353  			require.NoError(t, err)
  1354  			testQueryLog(t, logChan, "TestExecute", "DDL", stmt.input, 8)
  1355  		}
  1356  	}
  1357  }
  1358  
  1359  func TestExecutorDDLFk(t *testing.T) {
  1360  	executor, _, _, sbc := createExecutorEnv()
  1361  
  1362  	mName := "TestExecutorDDLFk"
  1363  	stmts := []string{
  1364  		"create table t1(id bigint primary key, foreign key (id) references t2(id))",
  1365  		"alter table t2 add foreign key (id) references t1(id) on delete cascade",
  1366  	}
  1367  
  1368  	for _, stmt := range stmts {
  1369  		for _, fkMode := range []string{"allow", "disallow"} {
  1370  			t.Run(stmt+fkMode, func(t *testing.T) {
  1371  				sbc.ExecCount.Set(0)
  1372  				foreignKeyMode = fkMode
  1373  				_, err := executor.Execute(ctx, mName, NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil)
  1374  				if fkMode == "allow" {
  1375  					require.NoError(t, err)
  1376  					require.EqualValues(t, 1, sbc.ExecCount.Get())
  1377  				} else {
  1378  					require.Error(t, err)
  1379  					require.Contains(t, err.Error(), "foreign key constraints are not allowed")
  1380  				}
  1381  			})
  1382  		}
  1383  	}
  1384  }
  1385  
  1386  func TestExecutorAlterVSchemaKeyspace(t *testing.T) {
  1387  	vschemaacl.AuthorizedDDLUsers = "%"
  1388  	defer func() {
  1389  		vschemaacl.AuthorizedDDLUsers = ""
  1390  	}()
  1391  	executor, _, _, _ := createExecutorEnv()
  1392  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true})
  1393  
  1394  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 2)
  1395  	executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
  1396  		vschemaUpdates <- vschema
  1397  		return true
  1398  	})
  1399  
  1400  	vschema := <-vschemaUpdates
  1401  	_, ok := vschema.Keyspaces["TestExecutor"].Vindexes["test_vindex"]
  1402  	if ok {
  1403  		t.Fatalf("test_vindex should not exist in original vschema")
  1404  	}
  1405  
  1406  	stmt := "alter vschema create vindex TestExecutor.test_vindex using hash"
  1407  	_, err := executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1408  	require.NoError(t, err)
  1409  
  1410  	_, vindex := waitForVindex(t, "TestExecutor", "test_vindex", vschemaUpdates, executor)
  1411  	assert.Equal(t, vindex.Type, "hash")
  1412  }
  1413  
  1414  func TestExecutorCreateVindexDDL(t *testing.T) {
  1415  	vschemaacl.AuthorizedDDLUsers = "%"
  1416  	defer func() {
  1417  		vschemaacl.AuthorizedDDLUsers = ""
  1418  	}()
  1419  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
  1420  	ks := "TestExecutor"
  1421  
  1422  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4)
  1423  	executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
  1424  		vschemaUpdates <- vschema
  1425  		return true
  1426  	})
  1427  
  1428  	vschema := <-vschemaUpdates
  1429  	_, ok := vschema.Keyspaces[ks].Vindexes["test_vindex"]
  1430  	if ok {
  1431  		t.Fatalf("test_vindex should not exist in original vschema")
  1432  	}
  1433  
  1434  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
  1435  	stmt := "alter vschema create vindex test_vindex using hash"
  1436  	_, err := executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1437  	require.NoError(t, err)
  1438  
  1439  	_, vindex := waitForVindex(t, ks, "test_vindex", vschemaUpdates, executor)
  1440  	if vindex == nil || vindex.Type != "hash" {
  1441  		t.Errorf("updated vschema did not contain test_vindex")
  1442  	}
  1443  
  1444  	_, err = executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1445  	wantErr := "vindex test_vindex already exists in keyspace TestExecutor"
  1446  	if err == nil || err.Error() != wantErr {
  1447  		t.Errorf("create duplicate vindex: %v, want %s", err, wantErr)
  1448  	}
  1449  	select {
  1450  	case <-vschemaUpdates:
  1451  		t.Error("vschema should not be updated on error")
  1452  	default:
  1453  	}
  1454  
  1455  	// Create a new vschema keyspace implicitly by creating a vindex with a different
  1456  	// target in the session
  1457  	// ksNew := "test_new_keyspace"
  1458  	session = NewSafeSession(&vtgatepb.Session{TargetString: ks})
  1459  	stmt = "alter vschema create vindex test_vindex2 using hash"
  1460  	_, err = executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1461  	if err != nil {
  1462  		t.Fatalf("error in %s: %v", stmt, err)
  1463  	}
  1464  
  1465  	vschema, vindex = waitForVindex(t, ks, "test_vindex2", vschemaUpdates, executor)
  1466  	if vindex.Type != "hash" {
  1467  		t.Errorf("vindex type %s not hash", vindex.Type)
  1468  	}
  1469  	keyspace, ok := vschema.Keyspaces[ks]
  1470  	if !ok || !keyspace.Sharded {
  1471  		t.Errorf("keyspace should have been created with Sharded=true")
  1472  	}
  1473  
  1474  	// No queries should have gone to any tablets
  1475  	wantCount := []int64{0, 0, 0}
  1476  	gotCount := []int64{
  1477  		sbc1.ExecCount.Get(),
  1478  		sbc2.ExecCount.Get(),
  1479  		sbclookup.ExecCount.Get(),
  1480  	}
  1481  	require.Equal(t, wantCount, gotCount)
  1482  }
  1483  
  1484  func TestExecutorAddDropVschemaTableDDL(t *testing.T) {
  1485  	vschemaacl.AuthorizedDDLUsers = "%"
  1486  	defer func() {
  1487  		vschemaacl.AuthorizedDDLUsers = ""
  1488  	}()
  1489  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
  1490  	ks := KsTestUnsharded
  1491  
  1492  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4)
  1493  	executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
  1494  		vschemaUpdates <- vschema
  1495  		return true
  1496  	})
  1497  
  1498  	vschema := <-vschemaUpdates
  1499  	_, ok := vschema.Keyspaces[ks].Tables["test_table"]
  1500  	if ok {
  1501  		t.Fatalf("test_table should not exist in original vschema")
  1502  	}
  1503  
  1504  	var vschemaTables []string
  1505  	for t := range vschema.Keyspaces[ks].Tables {
  1506  		vschemaTables = append(vschemaTables, t)
  1507  	}
  1508  
  1509  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
  1510  	stmt := "alter vschema add table test_table"
  1511  	_, err := executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1512  	require.NoError(t, err)
  1513  	_ = waitForVschemaTables(t, ks, append([]string{"test_table"}, vschemaTables...), executor)
  1514  
  1515  	stmt = "alter vschema add table test_table2"
  1516  	_, err = executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1517  	require.NoError(t, err)
  1518  	_ = waitForVschemaTables(t, ks, append([]string{"test_table", "test_table2"}, vschemaTables...), executor)
  1519  
  1520  	// Should fail adding a table on a sharded keyspace
  1521  	session = NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
  1522  	stmt = "alter vschema add table test_table"
  1523  	_, err = executor.Execute(ctx, "TestExecute", session, stmt, nil)
  1524  	require.EqualError(t, err, "add vschema table: unsupported on sharded keyspace TestExecutor")
  1525  
  1526  	// No queries should have gone to any tablets
  1527  	wantCount := []int64{0, 0, 0}
  1528  	gotCount := []int64{
  1529  		sbc1.ExecCount.Get(),
  1530  		sbc2.ExecCount.Get(),
  1531  		sbclookup.ExecCount.Get(),
  1532  	}
  1533  	utils.MustMatch(t, wantCount, gotCount, "")
  1534  }
  1535  
  1536  func TestExecutorVindexDDLACL(t *testing.T) {
  1537  	executor, _, _, _ := createExecutorEnv()
  1538  	ks := "TestExecutor"
  1539  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
  1540  
  1541  	ctxRedUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "redUser"})
  1542  	ctxBlueUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "blueUser"})
  1543  
  1544  	// test that by default no users can perform the operation
  1545  	stmt := "alter vschema create vindex test_hash using hash"
  1546  	_, err := executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil)
  1547  	require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`)
  1548  
  1549  	_, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil)
  1550  	require.EqualError(t, err, `User 'blueUser' is not authorized to perform vschema operations`)
  1551  
  1552  	// test when all users are enabled
  1553  	vschemaacl.AuthorizedDDLUsers = "%"
  1554  	vschemaacl.Init()
  1555  	_, err = executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil)
  1556  	if err != nil {
  1557  		t.Errorf("unexpected error '%v'", err)
  1558  	}
  1559  	stmt = "alter vschema create vindex test_hash2 using hash"
  1560  	_, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil)
  1561  	if err != nil {
  1562  		t.Errorf("unexpected error '%v'", err)
  1563  	}
  1564  
  1565  	// test when only one user is enabled
  1566  	vschemaacl.AuthorizedDDLUsers = "orangeUser, blueUser, greenUser"
  1567  	vschemaacl.Init()
  1568  	_, err = executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil)
  1569  	require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`)
  1570  
  1571  	stmt = "alter vschema create vindex test_hash3 using hash"
  1572  	_, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil)
  1573  	if err != nil {
  1574  		t.Errorf("unexpected error '%v'", err)
  1575  	}
  1576  
  1577  	// restore the disallowed state
  1578  	vschemaacl.AuthorizedDDLUsers = ""
  1579  }
  1580  
  1581  func TestExecutorUnrecognized(t *testing.T) {
  1582  	executor, _, _, _ := createExecutorEnv()
  1583  	_, err := executor.Execute(ctx, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "invalid statement", nil)
  1584  	require.Error(t, err, "unrecognized statement: invalid statement'")
  1585  }
  1586  
  1587  // TestVSchemaStats makes sure the building and displaying of the
  1588  // VSchemaStats works.
  1589  func TestVSchemaStats(t *testing.T) {
  1590  	r, _, _, _ := createExecutorEnv()
  1591  
  1592  	stats := r.VSchemaStats()
  1593  
  1594  	templ := template.New("")
  1595  	templ, err := templ.Parse(VSchemaTemplate)
  1596  	if err != nil {
  1597  		t.Fatalf("error parsing template: %v", err)
  1598  	}
  1599  	wr := &bytes.Buffer{}
  1600  	if err := templ.Execute(wr, stats); err != nil {
  1601  		t.Fatalf("error executing template: %v", err)
  1602  	}
  1603  	result := wr.String()
  1604  	if !strings.Contains(result, "<td>TestXBadSharding</td>") ||
  1605  		!strings.Contains(result, "<td>TestUnsharded</td>") {
  1606  		t.Errorf("invalid html result: %v", result)
  1607  	}
  1608  }
  1609  
  1610  var pv = querypb.ExecuteOptions_Gen4
  1611  
  1612  func TestGetPlanUnnormalized(t *testing.T) {
  1613  	r, _, _, _ := createExecutorEnv()
  1614  	emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1615  	unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1616  
  1617  	query1 := "select * from music_user_map where id = 1"
  1618  	plan1, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1619  	wantSQL := query1 + " /* comment */"
  1620  	if logStats1.SQL != wantSQL {
  1621  		t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats1.SQL)
  1622  	}
  1623  
  1624  	plan2, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1625  	if plan1 != plan2 {
  1626  		t.Errorf("getPlan(query1): plans must be equal: %p %p", plan1, plan2)
  1627  	}
  1628  	want := []string{
  1629  		"@unknown:" + query1,
  1630  	}
  1631  	assertCacheContains(t, r, want)
  1632  	if logStats2.SQL != wantSQL {
  1633  		t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats2.SQL)
  1634  	}
  1635  	plan3, logStats3 := getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1636  	if plan1 == plan3 {
  1637  		t.Errorf("getPlan(query1, ks): plans must not be equal: %p %p", plan1, plan3)
  1638  	}
  1639  	if logStats3.SQL != wantSQL {
  1640  		t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats3.SQL)
  1641  	}
  1642  	plan4, logStats4 := getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1643  	if plan3 != plan4 {
  1644  		t.Errorf("getPlan(query1, ks): plans must be equal: %p %p", plan3, plan4)
  1645  	}
  1646  	want = []string{
  1647  		KsTestUnsharded + "@unknown:" + query1,
  1648  		"@unknown:" + query1,
  1649  	}
  1650  	assertCacheContains(t, r, want)
  1651  	if logStats4.SQL != wantSQL {
  1652  		t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats4.SQL)
  1653  	}
  1654  }
  1655  
  1656  func assertCacheSize(t *testing.T, c cache.Cache, expected int) {
  1657  	t.Helper()
  1658  	var size int
  1659  	c.ForEach(func(_ any) bool {
  1660  		size++
  1661  		return true
  1662  	})
  1663  	if size != expected {
  1664  		t.Errorf("getPlan() expected cache to have size %d, but got: %d", expected, size)
  1665  	}
  1666  }
  1667  
  1668  func assertCacheContains(t *testing.T, e *Executor, want []string) {
  1669  	t.Helper()
  1670  	for _, wantKey := range want {
  1671  		if _, ok := e.debugGetPlan(wantKey); !ok {
  1672  			t.Errorf("missing key in plan cache: %v", wantKey)
  1673  		}
  1674  	}
  1675  }
  1676  
  1677  func getPlanCached(t *testing.T, e *Executor, vcursor *vcursorImpl, sql string, comments sqlparser.MarginComments, bindVars map[string]*querypb.BindVariable, skipQueryPlanCache bool) (*engine.Plan, *logstats.LogStats) {
  1678  	logStats := logstats.NewLogStats(ctx, "Test", "", "", nil)
  1679  	plan, _, err := e.getPlan(context.Background(), vcursor, sql, comments, bindVars, &SafeSession{
  1680  		Session: &vtgatepb.Session{Options: &querypb.ExecuteOptions{SkipQueryPlanCache: skipQueryPlanCache}},
  1681  	}, logStats)
  1682  	require.NoError(t, err)
  1683  
  1684  	// Wait for cache to settle
  1685  	e.plans.Wait()
  1686  	return plan, logStats
  1687  }
  1688  
  1689  func TestGetPlanCacheUnnormalized(t *testing.T) {
  1690  	r, _, _, _ := createExecutorEnv()
  1691  	emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1692  	query1 := "select * from music_user_map where id = 1"
  1693  
  1694  	_, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true)
  1695  	assertCacheSize(t, r.plans, 0)
  1696  
  1697  	wantSQL := query1 + " /* comment */"
  1698  	if logStats1.SQL != wantSQL {
  1699  		t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats1.SQL)
  1700  	}
  1701  
  1702  	_, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment 2 */"), map[string]*querypb.BindVariable{}, false)
  1703  	assertCacheSize(t, r.plans, 1)
  1704  
  1705  	wantSQL = query1 + " /* comment 2 */"
  1706  	if logStats2.SQL != wantSQL {
  1707  		t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats2.SQL)
  1708  	}
  1709  
  1710  	// Skip cache using directive
  1711  	r, _, _, _ = createExecutorEnv()
  1712  	unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1713  
  1714  	query1 = "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)"
  1715  	getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1716  	assertCacheSize(t, r.plans, 0)
  1717  
  1718  	query1 = "insert into user(id) values (1), (2)"
  1719  	getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1720  	assertCacheSize(t, r.plans, 1)
  1721  
  1722  	// the target string will be resolved and become part of the plan cache key, which adds a new entry
  1723  	ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1724  	getPlanCached(t, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1725  	assertCacheSize(t, r.plans, 2)
  1726  
  1727  	// the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above
  1728  	ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1729  	getPlanCached(t, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1730  	assertCacheSize(t, r.plans, 2)
  1731  }
  1732  
  1733  func TestGetPlanCacheNormalized(t *testing.T) {
  1734  	r, _, _, _ := createExecutorEnv()
  1735  	r.normalize = true
  1736  	emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1737  
  1738  	query1 := "select * from music_user_map where id = 1"
  1739  	_, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true /* skipQueryPlanCache */)
  1740  	assertCacheSize(t, r.plans, 0)
  1741  	wantSQL := "select * from music_user_map where id = :id /* comment */"
  1742  	assert.Equal(t, wantSQL, logStats1.SQL)
  1743  
  1744  	_, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false /* skipQueryPlanCache */)
  1745  	assertCacheSize(t, r.plans, 1)
  1746  	assert.Equal(t, wantSQL, logStats2.SQL)
  1747  
  1748  	// Skip cache using directive
  1749  	r, _, _, _ = createExecutorEnv()
  1750  	r.normalize = true
  1751  	unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1752  
  1753  	query1 = "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)"
  1754  	getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1755  	assertCacheSize(t, r.plans, 0)
  1756  
  1757  	query1 = "insert into user(id) values (1), (2)"
  1758  	getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1759  	assertCacheSize(t, r.plans, 1)
  1760  
  1761  	// the target string will be resolved and become part of the plan cache key, which adds a new entry
  1762  	ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1763  	getPlanCached(t, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1764  	assertCacheSize(t, r.plans, 2)
  1765  
  1766  	// the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above
  1767  	ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1768  	getPlanCached(t, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false)
  1769  	assertCacheSize(t, r.plans, 2)
  1770  }
  1771  
  1772  func TestGetPlanNormalized(t *testing.T) {
  1773  	r, _, _, _ := createExecutorEnv()
  1774  	r.normalize = true
  1775  	emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1776  	unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv)
  1777  
  1778  	query1 := "select * from music_user_map where id = 1"
  1779  	query2 := "select * from music_user_map where id = 2"
  1780  	normalized := "select * from music_user_map where id = :id"
  1781  
  1782  	plan1, logStats1 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment 1 */"), map[string]*querypb.BindVariable{}, false)
  1783  	plan2, logStats2 := getPlanCached(t, r, emptyvc, query1, makeComments(" /* comment 2 */"), map[string]*querypb.BindVariable{}, false)
  1784  
  1785  	assert.Equal(t, plan1, plan2)
  1786  	want := []string{
  1787  		"@unknown:" + normalized,
  1788  	}
  1789  	assertCacheContains(t, r, want)
  1790  
  1791  	wantSQL := normalized + " /* comment 1 */"
  1792  	assert.Equal(t, wantSQL, logStats1.SQL)
  1793  	wantSQL = normalized + " /* comment 2 */"
  1794  	assert.Equal(t, wantSQL, logStats2.SQL)
  1795  
  1796  	plan3, logStats3 := getPlanCached(t, r, emptyvc, query2, makeComments(" /* comment 3 */"), map[string]*querypb.BindVariable{}, false)
  1797  	assert.Equal(t, plan1, plan3)
  1798  	wantSQL = normalized + " /* comment 3 */"
  1799  	assert.Equal(t, wantSQL, logStats3.SQL)
  1800  
  1801  	plan4, logStats4 := getPlanCached(t, r, emptyvc, normalized, makeComments(" /* comment 4 */"), map[string]*querypb.BindVariable{}, false)
  1802  	assert.Equal(t, plan1, plan4)
  1803  	wantSQL = normalized + " /* comment 4 */"
  1804  	assert.Equal(t, wantSQL, logStats4.SQL)
  1805  
  1806  	var logStats5 *logstats.LogStats
  1807  	plan3, logStats5 = getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment 5 */"), map[string]*querypb.BindVariable{}, false)
  1808  	assert.Equal(t, plan1, plan3)
  1809  	wantSQL = normalized + " /* comment 5 */"
  1810  	assert.Equal(t, wantSQL, logStats5.SQL)
  1811  
  1812  	plan4, _ = getPlanCached(t, r, unshardedvc, query1, makeComments(" /* comment 6 */"), map[string]*querypb.BindVariable{}, false)
  1813  	assert.Equal(t, plan1, plan4)
  1814  	want = []string{
  1815  		KsTestUnsharded + "@unknown:" + normalized,
  1816  		"@unknown:" + normalized,
  1817  	}
  1818  	assertCacheContains(t, r, want)
  1819  
  1820  	_, _, err := r.getPlan(context.Background(), emptyvc, "syntax", makeComments(""), map[string]*querypb.BindVariable{}, nil, nil)
  1821  	assert.EqualError(t, err, "syntax error at position 7 near 'syntax'")
  1822  	assertCacheContains(t, r, want)
  1823  }
  1824  
  1825  func TestPassthroughDDL(t *testing.T) {
  1826  	executor, sbc1, sbc2, _ := createExecutorEnv()
  1827  	primarySession.TargetString = "TestExecutor"
  1828  
  1829  	alterDDL := "/* leading */ alter table passthrough_ddl add columne col bigint default 123 /* trailing */"
  1830  	_, err := executorExec(executor, alterDDL, nil)
  1831  	require.NoError(t, err)
  1832  	wantQueries := []*querypb.BoundQuery{{
  1833  		Sql:           alterDDL,
  1834  		BindVariables: map[string]*querypb.BindVariable{},
  1835  	}}
  1836  	if !reflect.DeepEqual(sbc1.Queries, wantQueries) {
  1837  		t.Errorf("sbc1.Queries: %+v, want %+v\n", sbc1.Queries, wantQueries)
  1838  	}
  1839  	if !reflect.DeepEqual(sbc2.Queries, wantQueries) {
  1840  		t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc2.Queries, wantQueries)
  1841  	}
  1842  	sbc1.Queries = nil
  1843  	sbc2.Queries = nil
  1844  
  1845  	// Force the query to go to only one shard. Normalization doesn't make any difference.
  1846  	primarySession.TargetString = "TestExecutor/40-60"
  1847  	executor.normalize = true
  1848  
  1849  	_, err = executorExec(executor, alterDDL, nil)
  1850  	require.NoError(t, err)
  1851  	require.Nil(t, sbc1.Queries)
  1852  	if !reflect.DeepEqual(sbc2.Queries, wantQueries) {
  1853  		t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc2.Queries, wantQueries)
  1854  	}
  1855  	sbc2.Queries = nil
  1856  	primarySession.TargetString = ""
  1857  
  1858  	// Use range query
  1859  	primarySession.TargetString = "TestExecutor[-]"
  1860  	executor.normalize = true
  1861  
  1862  	_, err = executorExec(executor, alterDDL, nil)
  1863  	require.NoError(t, err)
  1864  	if !reflect.DeepEqual(sbc1.Queries, wantQueries) {
  1865  		t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc1.Queries, wantQueries)
  1866  	}
  1867  	if !reflect.DeepEqual(sbc2.Queries, wantQueries) {
  1868  		t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc2.Queries, wantQueries)
  1869  	}
  1870  	sbc2.Queries = nil
  1871  	primarySession.TargetString = ""
  1872  }
  1873  
  1874  func TestParseEmptyTargetSingleKeyspace(t *testing.T) {
  1875  	r, _, _, _ := createExecutorEnv()
  1876  	altVSchema := &vindexes.VSchema{
  1877  		Keyspaces: map[string]*vindexes.KeyspaceSchema{
  1878  			KsTestUnsharded: r.vschema.Keyspaces[KsTestUnsharded],
  1879  		},
  1880  	}
  1881  	r.vschema = altVSchema
  1882  
  1883  	destKeyspace, destTabletType, _, _ := r.ParseDestinationTarget("")
  1884  	if destKeyspace != KsTestUnsharded || destTabletType != topodatapb.TabletType_PRIMARY {
  1885  		t.Errorf(
  1886  			"parseDestinationTarget(%s): got (%v, %v), want (%v, %v)",
  1887  			"@primary",
  1888  			destKeyspace,
  1889  			destTabletType,
  1890  			KsTestUnsharded,
  1891  			topodatapb.TabletType_PRIMARY,
  1892  		)
  1893  	}
  1894  }
  1895  
  1896  func TestParseEmptyTargetMultiKeyspace(t *testing.T) {
  1897  	r, _, _, _ := createExecutorEnv()
  1898  	altVSchema := &vindexes.VSchema{
  1899  		Keyspaces: map[string]*vindexes.KeyspaceSchema{
  1900  			KsTestUnsharded: r.vschema.Keyspaces[KsTestUnsharded],
  1901  			KsTestSharded:   r.vschema.Keyspaces[KsTestSharded],
  1902  		},
  1903  	}
  1904  	r.vschema = altVSchema
  1905  
  1906  	destKeyspace, destTabletType, _, _ := r.ParseDestinationTarget("")
  1907  	if destKeyspace != "" || destTabletType != topodatapb.TabletType_PRIMARY {
  1908  		t.Errorf(
  1909  			"parseDestinationTarget(%s): got (%v, %v), want (%v, %v)",
  1910  			"@primary",
  1911  			destKeyspace,
  1912  			destTabletType,
  1913  			"",
  1914  			topodatapb.TabletType_PRIMARY,
  1915  		)
  1916  	}
  1917  }
  1918  
  1919  func TestParseTargetSingleKeyspace(t *testing.T) {
  1920  	r, _, _, _ := createExecutorEnv()
  1921  	altVSchema := &vindexes.VSchema{
  1922  		Keyspaces: map[string]*vindexes.KeyspaceSchema{
  1923  			KsTestUnsharded: r.vschema.Keyspaces[KsTestUnsharded],
  1924  		},
  1925  	}
  1926  	r.vschema = altVSchema
  1927  
  1928  	destKeyspace, destTabletType, _, _ := r.ParseDestinationTarget("@replica")
  1929  	if destKeyspace != KsTestUnsharded || destTabletType != topodatapb.TabletType_REPLICA {
  1930  		t.Errorf(
  1931  			"parseDestinationTarget(%s): got (%v, %v), want (%v, %v)",
  1932  			"@replica",
  1933  			destKeyspace,
  1934  			destTabletType,
  1935  			KsTestUnsharded,
  1936  			topodatapb.TabletType_REPLICA,
  1937  		)
  1938  	}
  1939  }
  1940  
  1941  func TestDebugVSchema(t *testing.T) {
  1942  	resp := httptest.NewRecorder()
  1943  	req, _ := http.NewRequest("GET", "/debug/vschema", nil)
  1944  
  1945  	executor, _, _, _ := createExecutorEnv()
  1946  	executor.ServeHTTP(resp, req)
  1947  	v := make(map[string]any)
  1948  	if err := json.Unmarshal(resp.Body.Bytes(), &v); err != nil {
  1949  		t.Fatalf("Unmarshal on %s failed: %v", resp.Body.String(), err)
  1950  	}
  1951  	if _, ok := v["routing_rules"]; !ok {
  1952  		t.Errorf("routing rules missing: %v", resp.Body.String())
  1953  	}
  1954  	if _, ok := v["keyspaces"]; !ok {
  1955  		t.Errorf("keyspaces missing: %v", resp.Body.String())
  1956  	}
  1957  }
  1958  
  1959  func TestExecutorMaxPayloadSizeExceeded(t *testing.T) {
  1960  	saveMax := maxPayloadSize
  1961  	saveWarn := warnPayloadSize
  1962  	maxPayloadSize = 10
  1963  	warnPayloadSize = 5
  1964  	defer func() {
  1965  		maxPayloadSize = saveMax
  1966  		warnPayloadSize = saveWarn
  1967  	}()
  1968  
  1969  	executor, _, _, _ := createExecutorEnv()
  1970  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"})
  1971  	warningCount := warnings.Counts()["WarnPayloadSizeExceeded"]
  1972  	testMaxPayloadSizeExceeded := []string{
  1973  		"select * from main1",
  1974  		"insert into main1(id) values (1), (2)",
  1975  		"update main1 set id=1",
  1976  		"delete from main1 where id=1",
  1977  	}
  1978  	for _, query := range testMaxPayloadSizeExceeded {
  1979  		_, err := executor.Execute(context.Background(), "TestExecutorMaxPayloadSizeExceeded", session, query, nil)
  1980  		require.NotNil(t, err)
  1981  		assert.EqualError(t, err, "query payload size above threshold")
  1982  	}
  1983  	assert.Equal(t, warningCount, warnings.Counts()["WarnPayloadSizeExceeded"], "warnings count")
  1984  
  1985  	testMaxPayloadSizeOverride := []string{
  1986  		"select /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ * from main1",
  1987  		"insert /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ into main1(id) values (1), (2)",
  1988  		"update /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ main1 set id=1",
  1989  		"delete /*vt+ IGNORE_MAX_PAYLOAD_SIZE=1 */ from main1 where id=1",
  1990  	}
  1991  	for _, query := range testMaxPayloadSizeOverride {
  1992  		_, err := executor.Execute(context.Background(), "TestExecutorMaxPayloadSizeWithOverride", session, query, nil)
  1993  		assert.Equal(t, nil, err, "err should be nil")
  1994  	}
  1995  	assert.Equal(t, warningCount, warnings.Counts()["WarnPayloadSizeExceeded"], "warnings count")
  1996  
  1997  	maxPayloadSize = 1000
  1998  	for _, query := range testMaxPayloadSizeExceeded {
  1999  		_, err := executor.Execute(context.Background(), "TestExecutorMaxPayloadSizeExceeded", session, query, nil)
  2000  		assert.Equal(t, nil, err, "err should be nil")
  2001  	}
  2002  	assert.Equal(t, warningCount+4, warnings.Counts()["WarnPayloadSizeExceeded"], "warnings count")
  2003  }
  2004  
  2005  func TestOlapSelectDatabase(t *testing.T) {
  2006  	executor, _, _, _ := createExecutorEnv()
  2007  	executor.normalize = true
  2008  
  2009  	session := &vtgatepb.Session{Autocommit: true}
  2010  
  2011  	sql := `select database()`
  2012  	cbInvoked := false
  2013  	cb := func(r *sqltypes.Result) error {
  2014  		cbInvoked = true
  2015  		return nil
  2016  	}
  2017  	err := executor.StreamExecute(context.Background(), "TestExecute", NewSafeSession(session), sql, nil, cb)
  2018  	assert.NoError(t, err)
  2019  	assert.True(t, cbInvoked)
  2020  }
  2021  
  2022  func TestExecutorClearsWarnings(t *testing.T) {
  2023  	executor, _, _, _ := createExecutorEnv()
  2024  	session := NewSafeSession(&vtgatepb.Session{
  2025  		Warnings: []*querypb.QueryWarning{{Code: 234, Message: "oh noes"}},
  2026  	})
  2027  	_, err := executor.Execute(context.Background(), "TestExecute", session, "select 42", nil)
  2028  	require.NoError(t, err)
  2029  	require.Empty(t, session.Warnings)
  2030  }
  2031  
  2032  func TestExecutorOtherRead(t *testing.T) {
  2033  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
  2034  
  2035  	type cnts struct {
  2036  		Sbc1Cnt      int64
  2037  		Sbc2Cnt      int64
  2038  		SbcLookupCnt int64
  2039  	}
  2040  
  2041  	tcs := []struct {
  2042  		targetStr string
  2043  
  2044  		hasNoKeyspaceErr       bool
  2045  		hasDestinationShardErr bool
  2046  		wantCnts               cnts
  2047  	}{
  2048  		{
  2049  			targetStr:        "",
  2050  			hasNoKeyspaceErr: true,
  2051  		},
  2052  		{
  2053  			targetStr:              "TestExecutor[-]",
  2054  			hasDestinationShardErr: true,
  2055  		},
  2056  		{
  2057  			targetStr: KsTestUnsharded,
  2058  			wantCnts: cnts{
  2059  				Sbc1Cnt:      0,
  2060  				Sbc2Cnt:      0,
  2061  				SbcLookupCnt: 1,
  2062  			},
  2063  		},
  2064  		{
  2065  			targetStr: "TestExecutor",
  2066  			wantCnts: cnts{
  2067  				Sbc1Cnt:      1,
  2068  				Sbc2Cnt:      0,
  2069  				SbcLookupCnt: 0,
  2070  			},
  2071  		},
  2072  	}
  2073  
  2074  	stmts := []string{
  2075  		"analyze table t1",
  2076  		"describe select * from t1",
  2077  		"explain select * from t1",
  2078  		"do 1",
  2079  	}
  2080  
  2081  	for _, stmt := range stmts {
  2082  		for _, tc := range tcs {
  2083  			t.Run(stmt+tc.targetStr, func(t *testing.T) {
  2084  				sbc1.ExecCount.Set(0)
  2085  				sbc2.ExecCount.Set(0)
  2086  				sbclookup.ExecCount.Set(0)
  2087  
  2088  				_, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil)
  2089  				if tc.hasNoKeyspaceErr {
  2090  					assert.EqualError(t, err, errNoKeyspace.Error())
  2091  				} else if tc.hasDestinationShardErr {
  2092  					assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt)
  2093  				} else {
  2094  					assert.NoError(t, err)
  2095  				}
  2096  
  2097  				utils.MustMatch(t, tc.wantCnts, cnts{
  2098  					Sbc1Cnt:      sbc1.ExecCount.Get(),
  2099  					Sbc2Cnt:      sbc2.ExecCount.Get(),
  2100  					SbcLookupCnt: sbclookup.ExecCount.Get(),
  2101  				}, "count did not match")
  2102  			})
  2103  		}
  2104  	}
  2105  }
  2106  
  2107  func TestExecutorVExplain(t *testing.T) {
  2108  	executor, _, _, _ := createExecutorEnv()
  2109  	executor.normalize = true
  2110  	logChan := QueryLogger.Subscribe("Test")
  2111  	defer QueryLogger.Unsubscribe(logChan)
  2112  
  2113  	bindVars := map[string]*querypb.BindVariable{}
  2114  	result, err := executorExec(executor, "vexplain plan select * from user", bindVars)
  2115  	require.NoError(t, err)
  2116  
  2117  	require.Equal(t,
  2118  		`[[VARCHAR("{\n\t\"OperatorType\": \"Route\",\n\t\"Variant\": \"Scatter\",\n\t\"Keyspace\": {\n\t\t\"Name\": \"TestExecutor\",\n\t\t\"Sharded\": true\n\t},\n\t\"FieldQuery\": \"select * from `+"`user`"+` where 1 != 1\",\n\t\"Query\": \"select * from `+"`user`"+`\",\n\t\"Table\": \"`+"`user`"+`\"\n}")]]`,
  2119  		fmt.Sprintf("%v", result.Rows))
  2120  
  2121  	result, err = executorExec(executor, "vexplain plan select 42", bindVars)
  2122  	require.NoError(t, err)
  2123  	expected := `[[VARCHAR("{\n\t\"OperatorType\": \"Projection\",\n\t\"Expressions\": [\n\t\t\"INT64(42) as 42\"\n\t],\n\t\"Inputs\": [\n\t\t{\n\t\t\t\"OperatorType\": \"SingleRow\"\n\t\t}\n\t]\n}")]]`
  2124  	require.Equal(t, expected, fmt.Sprintf("%v", result.Rows))
  2125  }
  2126  
  2127  func TestExecutorOtherAdmin(t *testing.T) {
  2128  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
  2129  
  2130  	type cnts struct {
  2131  		Sbc1Cnt      int64
  2132  		Sbc2Cnt      int64
  2133  		SbcLookupCnt int64
  2134  	}
  2135  
  2136  	tcs := []struct {
  2137  		targetStr string
  2138  
  2139  		hasNoKeyspaceErr       bool
  2140  		hasDestinationShardErr bool
  2141  		wantCnts               cnts
  2142  	}{
  2143  		{
  2144  			targetStr:        "",
  2145  			hasNoKeyspaceErr: true,
  2146  		},
  2147  		{
  2148  			targetStr:              "TestExecutor[-]",
  2149  			hasDestinationShardErr: true,
  2150  		},
  2151  		{
  2152  			targetStr: KsTestUnsharded,
  2153  			wantCnts: cnts{
  2154  				Sbc1Cnt:      0,
  2155  				Sbc2Cnt:      0,
  2156  				SbcLookupCnt: 1,
  2157  			},
  2158  		},
  2159  		{
  2160  			targetStr: "TestExecutor",
  2161  			wantCnts: cnts{
  2162  				Sbc1Cnt:      1,
  2163  				Sbc2Cnt:      0,
  2164  				SbcLookupCnt: 0,
  2165  			},
  2166  		},
  2167  	}
  2168  
  2169  	stmts := []string{
  2170  		"repair table t1",
  2171  		"optimize table t1",
  2172  	}
  2173  
  2174  	for _, stmt := range stmts {
  2175  		for _, tc := range tcs {
  2176  			sbc1.ExecCount.Set(0)
  2177  			sbc2.ExecCount.Set(0)
  2178  			sbclookup.ExecCount.Set(0)
  2179  
  2180  			_, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil)
  2181  			if tc.hasNoKeyspaceErr {
  2182  				assert.Error(t, err, errNoKeyspace)
  2183  			} else if tc.hasDestinationShardErr {
  2184  				assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt)
  2185  			} else {
  2186  				assert.NoError(t, err)
  2187  			}
  2188  
  2189  			diff := cmp.Diff(tc.wantCnts, cnts{
  2190  				Sbc1Cnt:      sbc1.ExecCount.Get(),
  2191  				Sbc2Cnt:      sbc2.ExecCount.Get(),
  2192  				SbcLookupCnt: sbclookup.ExecCount.Get(),
  2193  			})
  2194  			if diff != "" {
  2195  				t.Errorf("stmt: %s\ntc: %+v\n-want,+got:\n%s", stmt, tc, diff)
  2196  			}
  2197  		}
  2198  	}
  2199  }
  2200  
  2201  func TestExecutorSavepointInTx(t *testing.T) {
  2202  	executor, sbc1, sbc2, _ := createExecutorEnv()
  2203  	logChan := QueryLogger.Subscribe("TestExecutorSavepoint")
  2204  	defer QueryLogger.Unsubscribe(logChan)
  2205  
  2206  	session := NewSafeSession(&vtgatepb.Session{Autocommit: false, TargetString: "@primary"})
  2207  	_, err := exec(executor, session, "savepoint a")
  2208  	require.NoError(t, err)
  2209  	_, err = exec(executor, session, "rollback to a")
  2210  	require.NoError(t, err)
  2211  	_, err = exec(executor, session, "release savepoint a")
  2212  	require.NoError(t, err)
  2213  	_, err = exec(executor, session, "select id from user where id = 1")
  2214  	require.NoError(t, err)
  2215  	_, err = exec(executor, session, "savepoint b")
  2216  	require.NoError(t, err)
  2217  	_, err = exec(executor, session, "rollback to b")
  2218  	require.NoError(t, err)
  2219  	_, err = exec(executor, session, "release savepoint b")
  2220  	require.NoError(t, err)
  2221  	_, err = exec(executor, session, "select id from user where id = 3")
  2222  	require.NoError(t, err)
  2223  	_, err = exec(executor, session, "rollback")
  2224  	require.NoError(t, err)
  2225  	sbc1WantQueries := []*querypb.BoundQuery{{
  2226  		Sql:           "savepoint a",
  2227  		BindVariables: map[string]*querypb.BindVariable{},
  2228  	}, {
  2229  		Sql:           "rollback to a",
  2230  		BindVariables: map[string]*querypb.BindVariable{},
  2231  	}, {
  2232  		Sql:           "release savepoint a",
  2233  		BindVariables: map[string]*querypb.BindVariable{},
  2234  	}, {
  2235  		Sql:           "select id from `user` where id = 1",
  2236  		BindVariables: map[string]*querypb.BindVariable{},
  2237  	}, {
  2238  		Sql:           "savepoint b",
  2239  		BindVariables: map[string]*querypb.BindVariable{},
  2240  	}, {
  2241  		Sql:           "rollback to b",
  2242  		BindVariables: map[string]*querypb.BindVariable{},
  2243  	}, {
  2244  		Sql:           "release savepoint b",
  2245  		BindVariables: map[string]*querypb.BindVariable{},
  2246  	}}
  2247  
  2248  	sbc2WantQueries := []*querypb.BoundQuery{{
  2249  		Sql:           "savepoint a",
  2250  		BindVariables: map[string]*querypb.BindVariable{},
  2251  	}, {
  2252  		Sql:           "rollback to a",
  2253  		BindVariables: map[string]*querypb.BindVariable{},
  2254  	}, {
  2255  		Sql:           "release savepoint a",
  2256  		BindVariables: map[string]*querypb.BindVariable{},
  2257  	}, {
  2258  		Sql:           "savepoint b",
  2259  		BindVariables: map[string]*querypb.BindVariable{},
  2260  	}, {
  2261  		Sql:           "rollback to b",
  2262  		BindVariables: map[string]*querypb.BindVariable{},
  2263  	}, {
  2264  		Sql:           "release savepoint b",
  2265  		BindVariables: map[string]*querypb.BindVariable{},
  2266  	}, {
  2267  		Sql:           "select id from `user` where id = 3",
  2268  		BindVariables: map[string]*querypb.BindVariable{},
  2269  	}}
  2270  	utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "")
  2271  	utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "")
  2272  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint a", 0)
  2273  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to a", 0)
  2274  	testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint a", 0)
  2275  	testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 1", 1)
  2276  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint b", 1)
  2277  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to b", 1)
  2278  	testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint b", 1)
  2279  	testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 3", 1)
  2280  	testQueryLog(t, logChan, "TestExecute", "ROLLBACK", "rollback", 2)
  2281  }
  2282  
  2283  func TestExecutorSavepointInTxWithReservedConn(t *testing.T) {
  2284  	executor, sbc1, sbc2, _ := createExecutorEnv()
  2285  	logChan := QueryLogger.Subscribe("TestExecutorSavepoint")
  2286  	defer QueryLogger.Unsubscribe(logChan)
  2287  
  2288  	session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "TestExecutor", EnableSystemSettings: true})
  2289  	sbc1.SetResults([]*sqltypes.Result{
  2290  		sqltypes.MakeTestResult(sqltypes.MakeTestFields("orig|new", "varchar|varchar"), "a|"),
  2291  	})
  2292  	_, err := exec(executor, session, "set sql_mode = ''")
  2293  	require.NoError(t, err)
  2294  
  2295  	_, err = exec(executor, session, "begin")
  2296  	require.NoError(t, err)
  2297  	_, err = exec(executor, session, "savepoint a")
  2298  	require.NoError(t, err)
  2299  	_, err = exec(executor, session, "select id from user where id = 1")
  2300  	require.NoError(t, err)
  2301  	_, err = exec(executor, session, "savepoint b")
  2302  	require.NoError(t, err)
  2303  	_, err = exec(executor, session, "release savepoint a")
  2304  	require.NoError(t, err)
  2305  	_, err = exec(executor, session, "select id from user where id = 3")
  2306  	require.NoError(t, err)
  2307  	_, err = exec(executor, session, "commit")
  2308  	require.NoError(t, err)
  2309  	emptyBV := map[string]*querypb.BindVariable{}
  2310  
  2311  	sbc1WantQueries := []*querypb.BoundQuery{{
  2312  		Sql: "select @@sql_mode orig, '' new", BindVariables: emptyBV,
  2313  	}, {
  2314  		Sql: "set sql_mode = ''", BindVariables: emptyBV,
  2315  	}, {
  2316  		Sql: "savepoint a", BindVariables: emptyBV,
  2317  	}, {
  2318  		Sql: "select id from `user` where id = 1", BindVariables: emptyBV,
  2319  	}, {
  2320  		Sql: "savepoint b", BindVariables: emptyBV,
  2321  	}, {
  2322  		Sql: "release savepoint a", BindVariables: emptyBV,
  2323  	}}
  2324  
  2325  	sbc2WantQueries := []*querypb.BoundQuery{{
  2326  		Sql: "set sql_mode = ''", BindVariables: emptyBV,
  2327  	}, {
  2328  		Sql: "savepoint a", BindVariables: emptyBV,
  2329  	}, {
  2330  		Sql: "savepoint b", BindVariables: emptyBV,
  2331  	}, {
  2332  		Sql: "release savepoint a", BindVariables: emptyBV,
  2333  	}, {
  2334  		Sql: "select id from `user` where id = 3", BindVariables: emptyBV,
  2335  	}}
  2336  
  2337  	utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "")
  2338  	utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "")
  2339  	testQueryLog(t, logChan, "TestExecute", "SET", "set @@sql_mode = ''", 1)
  2340  	testQueryLog(t, logChan, "TestExecute", "BEGIN", "begin", 0)
  2341  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint a", 0)
  2342  	testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 1", 1)
  2343  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint b", 1)
  2344  	testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint a", 1)
  2345  	testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 3", 1)
  2346  	testQueryLog(t, logChan, "TestExecute", "COMMIT", "commit", 2)
  2347  }
  2348  
  2349  func TestExecutorSavepointWithoutTx(t *testing.T) {
  2350  	executor, sbc1, sbc2, _ := createExecutorEnv()
  2351  	logChan := QueryLogger.Subscribe("TestExecutorSavepoint")
  2352  	defer QueryLogger.Unsubscribe(logChan)
  2353  
  2354  	session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary", InTransaction: false})
  2355  	_, err := exec(executor, session, "savepoint a")
  2356  	require.NoError(t, err)
  2357  	_, err = exec(executor, session, "rollback to a")
  2358  	require.Error(t, err)
  2359  	_, err = exec(executor, session, "release savepoint a")
  2360  	require.Error(t, err)
  2361  	_, err = exec(executor, session, "select id from user where id = 1")
  2362  	require.NoError(t, err)
  2363  	_, err = exec(executor, session, "savepoint b")
  2364  	require.NoError(t, err)
  2365  	_, err = exec(executor, session, "rollback to b")
  2366  	require.Error(t, err)
  2367  	_, err = exec(executor, session, "release savepoint b")
  2368  	require.Error(t, err)
  2369  	_, err = exec(executor, session, "select id from user where id = 3")
  2370  	require.NoError(t, err)
  2371  	sbc1WantQueries := []*querypb.BoundQuery{{
  2372  		Sql:           "select id from `user` where id = 1",
  2373  		BindVariables: map[string]*querypb.BindVariable{},
  2374  	}}
  2375  
  2376  	sbc2WantQueries := []*querypb.BoundQuery{{
  2377  		Sql:           "select id from `user` where id = 3",
  2378  		BindVariables: map[string]*querypb.BindVariable{},
  2379  	}}
  2380  	utils.MustMatch(t, sbc1WantQueries, sbc1.Queries, "")
  2381  	utils.MustMatch(t, sbc2WantQueries, sbc2.Queries, "")
  2382  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint a", 0)
  2383  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to a", 0)
  2384  	testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint a", 0)
  2385  	testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 1", 1)
  2386  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT", "savepoint b", 0)
  2387  	testQueryLog(t, logChan, "TestExecute", "SAVEPOINT_ROLLBACK", "rollback to b", 0)
  2388  	testQueryLog(t, logChan, "TestExecute", "RELEASE", "release savepoint b", 0)
  2389  	testQueryLog(t, logChan, "TestExecute", "SELECT", "select id from user where id = 3", 1)
  2390  }
  2391  
  2392  func TestExecutorCallProc(t *testing.T) {
  2393  	executor, sbc1, sbc2, sbcUnsharded := createExecutorEnv()
  2394  
  2395  	type cnts struct {
  2396  		Sbc1Cnt      int64
  2397  		Sbc2Cnt      int64
  2398  		SbcUnsharded int64
  2399  	}
  2400  
  2401  	tcs := []struct {
  2402  		name, targetStr string
  2403  
  2404  		hasNoKeyspaceErr bool
  2405  		unshardedOnlyErr bool
  2406  		wantCnts         cnts
  2407  	}{{
  2408  		name:             "simple call with no keyspace set",
  2409  		targetStr:        "",
  2410  		hasNoKeyspaceErr: true,
  2411  	}, {
  2412  		name:      "keyrange targeted keyspace",
  2413  		targetStr: "TestExecutor[-]",
  2414  		wantCnts: cnts{
  2415  			Sbc1Cnt:      1,
  2416  			Sbc2Cnt:      1,
  2417  			SbcUnsharded: 0,
  2418  		},
  2419  	}, {
  2420  		name:      "unsharded call proc",
  2421  		targetStr: KsTestUnsharded,
  2422  		wantCnts: cnts{
  2423  			Sbc1Cnt:      0,
  2424  			Sbc2Cnt:      0,
  2425  			SbcUnsharded: 1,
  2426  		},
  2427  	}, {
  2428  		name:             "should fail with sharded call proc",
  2429  		targetStr:        "TestExecutor",
  2430  		unshardedOnlyErr: true,
  2431  	}}
  2432  
  2433  	for _, tc := range tcs {
  2434  		t.Run(tc.name, func(t *testing.T) {
  2435  			sbc1.ExecCount.Set(0)
  2436  			sbc2.ExecCount.Set(0)
  2437  			sbcUnsharded.ExecCount.Set(0)
  2438  
  2439  			_, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), "CALL proc()", nil)
  2440  			if tc.hasNoKeyspaceErr {
  2441  				assert.EqualError(t, err, errNoKeyspace.Error())
  2442  			} else if tc.unshardedOnlyErr {
  2443  				require.EqualError(t, err, "CALL is not supported for sharded keyspace")
  2444  			} else {
  2445  				assert.NoError(t, err)
  2446  			}
  2447  
  2448  			utils.MustMatch(t, tc.wantCnts, cnts{
  2449  				Sbc1Cnt:      sbc1.ExecCount.Get(),
  2450  				Sbc2Cnt:      sbc2.ExecCount.Get(),
  2451  				SbcUnsharded: sbcUnsharded.ExecCount.Get(),
  2452  			}, "count did not match")
  2453  		})
  2454  	}
  2455  }
  2456  
  2457  func TestExecutorTempTable(t *testing.T) {
  2458  	executor, _, _, sbcUnsharded := createExecutorEnv()
  2459  	executor.warnShardedOnly = true
  2460  	creatQuery := "create temporary table temp_t(id bigint primary key)"
  2461  	session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded})
  2462  	ctx := context.Background()
  2463  	_, err := executor.Execute(ctx, "TestExecutorTempTable", session, creatQuery, nil)
  2464  	require.NoError(t, err)
  2465  	assert.EqualValues(t, 1, sbcUnsharded.ExecCount.Get())
  2466  	assert.NotEmpty(t, session.Warnings)
  2467  
  2468  	before := executor.plans.Len()
  2469  
  2470  	_, err = executor.Execute(ctx, "TestExecutorTempTable", session, "select * from temp_t", nil)
  2471  	require.NoError(t, err)
  2472  
  2473  	assert.Equal(t, before, executor.plans.Len())
  2474  }
  2475  
  2476  func TestExecutorShowVitessMigrations(t *testing.T) {
  2477  	executor, sbc1, sbc2, _ := createExecutorEnv()
  2478  	showQuery := "show vitess_migrations"
  2479  	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
  2480  	ctx := context.Background()
  2481  	_, err := executor.Execute(ctx, "", session, showQuery, nil)
  2482  	require.NoError(t, err)
  2483  	assert.Contains(t, sbc1.StringQueries(), "SELECT * FROM _vt.schema_migrations")
  2484  	assert.Contains(t, sbc2.StringQueries(), "SELECT * FROM _vt.schema_migrations")
  2485  }
  2486  
  2487  func TestExecutorDescHash(t *testing.T) {
  2488  	executor, _, _, _ := createExecutorEnv()
  2489  	showQuery := "desc hash_index"
  2490  	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
  2491  	ctx := context.Background()
  2492  	_, err := executor.Execute(ctx, "", session, showQuery, nil)
  2493  	require.NoError(t, err)
  2494  }
  2495  
  2496  func TestExecutorVExplainQueries(t *testing.T) {
  2497  	executor, _, _, sbclookup := createExecutorEnv()
  2498  	session := NewAutocommitSession(&vtgatepb.Session{})
  2499  
  2500  	sbclookup.SetResults([]*sqltypes.Result{
  2501  		sqltypes.MakeTestResult(sqltypes.MakeTestFields("name|user_id", "varchar|int64"), "apa|1", "apa|2"),
  2502  	})
  2503  	qr, err := executor.Execute(ctx, "TestExecutorVExplainQueries", session, "vexplain queries select * from user where name = 'apa'", nil)
  2504  	require.NoError(t, err)
  2505  	txt := fmt.Sprintf("%v\n", qr.Rows)
  2506  	lookupQuery := "select `name`, user_id from name_user_map where `name` in"
  2507  	require.Contains(t, txt, lookupQuery)
  2508  
  2509  	// Test the streaming side as well
  2510  	var results []sqltypes.Row
  2511  	session = NewAutocommitSession(&vtgatepb.Session{})
  2512  	err = executor.StreamExecute(ctx, "TestExecutorVExplainQueries", session, "vexplain queries select * from user where name = 'apa'", nil, func(result *sqltypes.Result) error {
  2513  		results = append(results, result.Rows...)
  2514  		return nil
  2515  	})
  2516  	require.NoError(t, err)
  2517  	txt = fmt.Sprintf("%v\n", results)
  2518  	require.Contains(t, txt, lookupQuery)
  2519  }
  2520  
  2521  func TestExecutorStartTxnStmt(t *testing.T) {
  2522  	executor, _, _, _ := createExecutorEnv()
  2523  	session := NewAutocommitSession(&vtgatepb.Session{})
  2524  
  2525  	tcases := []struct {
  2526  		beginSQL        string
  2527  		expTxAccessMode []querypb.ExecuteOptions_TransactionAccessMode
  2528  	}{{
  2529  		beginSQL: "begin",
  2530  	}, {
  2531  		beginSQL: "start transaction",
  2532  	}, {
  2533  		beginSQL:        "start transaction with consistent snapshot",
  2534  		expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_CONSISTENT_SNAPSHOT},
  2535  	}, {
  2536  		beginSQL:        "start transaction read only",
  2537  		expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_ONLY},
  2538  	}, {
  2539  		beginSQL:        "start transaction read write",
  2540  		expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_WRITE},
  2541  	}, {
  2542  		beginSQL:        "start transaction with consistent snapshot, read only",
  2543  		expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_CONSISTENT_SNAPSHOT, querypb.ExecuteOptions_READ_ONLY},
  2544  	}, {
  2545  		beginSQL:        "start transaction with consistent snapshot, read write",
  2546  		expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_CONSISTENT_SNAPSHOT, querypb.ExecuteOptions_READ_WRITE},
  2547  	}, {
  2548  		beginSQL:        "start transaction read only, with consistent snapshot",
  2549  		expTxAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_ONLY, querypb.ExecuteOptions_CONSISTENT_SNAPSHOT},
  2550  	}}
  2551  
  2552  	for _, tcase := range tcases {
  2553  		t.Run(tcase.beginSQL, func(t *testing.T) {
  2554  			_, err := executor.Execute(ctx, "TestExecutorStartTxnStmt", session, tcase.beginSQL, nil)
  2555  			require.NoError(t, err)
  2556  
  2557  			assert.Equal(t, tcase.expTxAccessMode, session.GetOrCreateOptions().TransactionAccessMode)
  2558  
  2559  			_, err = executor.Execute(ctx, "TestExecutorStartTxnStmt", session, "rollback", nil)
  2560  			require.NoError(t, err)
  2561  
  2562  		})
  2563  	}
  2564  }
  2565  
  2566  func exec(executor *Executor, session *SafeSession, sql string) (*sqltypes.Result, error) {
  2567  	return executor.Execute(context.Background(), "TestExecute", session, sql, nil)
  2568  }
  2569  
  2570  func makeComments(text string) sqlparser.MarginComments {
  2571  	return sqlparser.MarginComments{Trailing: text}
  2572  }