vitess.io/vitess@v0.16.2/go/vt/vtgate/executor_vschema_ddl_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  	"reflect"
    21  	"sort"
    22  	"testing"
    23  	"time"
    24  
    25  	"vitess.io/vitess/go/test/utils"
    26  
    27  	"vitess.io/vitess/go/vt/callerid"
    28  	querypb "vitess.io/vitess/go/vt/proto/query"
    29  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    30  
    31  	"context"
    32  
    33  	"vitess.io/vitess/go/sqltypes"
    34  	"vitess.io/vitess/go/vt/vtgate/vschemaacl"
    35  
    36  	"github.com/stretchr/testify/assert"
    37  	"github.com/stretchr/testify/require"
    38  
    39  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    40  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    41  )
    42  
    43  func waitForVindex(t *testing.T, ks, name string, watch chan *vschemapb.SrvVSchema, executor *Executor) (*vschemapb.SrvVSchema, *vschemapb.Vindex) {
    44  	t.Helper()
    45  
    46  	// Wait up to 10ms until the watch gets notified of the update
    47  	ok := false
    48  	for i := 0; i < 10; i++ {
    49  		select {
    50  		case vschema := <-watch:
    51  			_, ok = vschema.Keyspaces[ks].Vindexes[name]
    52  			if !ok {
    53  				t.Errorf("updated vschema did not contain %s", name)
    54  			}
    55  		default:
    56  			time.Sleep(time.Millisecond)
    57  		}
    58  	}
    59  	if !ok {
    60  		t.Errorf("vschema was not updated as expected")
    61  	}
    62  
    63  	// Wait up to 100ms until the vindex manager gets notified of the update
    64  	for i := 0; i < 10; i++ {
    65  		vschema := executor.vm.GetCurrentSrvVschema()
    66  		vindex, ok := vschema.Keyspaces[ks].Vindexes[name]
    67  		if ok {
    68  			return vschema, vindex
    69  		}
    70  		time.Sleep(10 * time.Millisecond)
    71  	}
    72  
    73  	t.Fatalf("updated vschema did not contain %s", name)
    74  	return nil, nil
    75  }
    76  
    77  func waitForVschemaTables(t *testing.T, ks string, tables []string, executor *Executor) *vschemapb.SrvVSchema {
    78  	t.Helper()
    79  
    80  	// Wait up to 100ms until the vindex manager gets notified of the update
    81  	for i := 0; i < 10; i++ {
    82  		vschema := executor.vm.GetCurrentSrvVschema()
    83  		gotTables := []string{}
    84  		for t := range vschema.Keyspaces[ks].Tables {
    85  			gotTables = append(gotTables, t)
    86  		}
    87  		sort.Strings(tables)
    88  		sort.Strings(gotTables)
    89  		if reflect.DeepEqual(tables, gotTables) {
    90  			return vschema
    91  		}
    92  		time.Sleep(10 * time.Millisecond)
    93  	}
    94  
    95  	t.Fatalf("updated vschema did not contain tables %v", tables)
    96  	return nil
    97  }
    98  
    99  // nolint
   100  func waitForColVindexes(t *testing.T, ks, table string, names []string, executor *Executor) *vschemapb.SrvVSchema {
   101  	t.Helper()
   102  
   103  	// Wait up to 10ms until the vindex manager gets notified of the update
   104  	for i := 0; i < 10; i++ {
   105  
   106  		vschema := executor.vm.GetCurrentSrvVschema()
   107  		table, ok := vschema.Keyspaces[ks].Tables[table]
   108  
   109  		// The table is removed from the vschema when there are no
   110  		// vindexes defined
   111  		if !ok == (len(names) == 0) {
   112  			return vschema
   113  		} else if ok && (len(names) == len(table.ColumnVindexes)) {
   114  			match := true
   115  			for i, name := range names {
   116  				if name != table.ColumnVindexes[i].Name {
   117  					match = false
   118  					break
   119  				}
   120  			}
   121  			if match {
   122  				return vschema
   123  			}
   124  		}
   125  
   126  		time.Sleep(time.Millisecond)
   127  
   128  	}
   129  
   130  	t.Fatalf("updated vschema did not contain vindexes %v on table %s", names, table)
   131  	return nil
   132  }
   133  
   134  func TestPlanExecutorAlterVSchemaKeyspace(t *testing.T) {
   135  	vschemaacl.AuthorizedDDLUsers = "%"
   136  	defer func() {
   137  		vschemaacl.AuthorizedDDLUsers = ""
   138  	}()
   139  	executor, _, _, _ := createExecutorEnv()
   140  	session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true})
   141  
   142  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 2)
   143  	executor.serv.WatchSrvVSchema(context.Background(), "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
   144  		vschemaUpdates <- vschema
   145  		return true
   146  	})
   147  
   148  	vschema := <-vschemaUpdates
   149  	_, ok := vschema.Keyspaces["TestExecutor"].Vindexes["test_vindex"]
   150  	if ok {
   151  		t.Fatalf("test_vindex should not exist in original vschema")
   152  	}
   153  
   154  	stmt := "alter vschema create vindex TestExecutor.test_vindex using hash"
   155  	_, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   156  	require.NoError(t, err)
   157  
   158  	_, vindex := waitForVindex(t, "TestExecutor", "test_vindex", vschemaUpdates, executor)
   159  	assert.Equal(t, vindex.Type, "hash")
   160  }
   161  
   162  func TestPlanExecutorCreateVindexDDL(t *testing.T) {
   163  	vschemaacl.AuthorizedDDLUsers = "%"
   164  	defer func() {
   165  		vschemaacl.AuthorizedDDLUsers = ""
   166  	}()
   167  	executor, _, _, _ := createExecutorEnv()
   168  	ks := "TestExecutor"
   169  
   170  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4)
   171  	executor.serv.WatchSrvVSchema(context.Background(), "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
   172  		vschemaUpdates <- vschema
   173  		return true
   174  	})
   175  
   176  	vschema := <-vschemaUpdates
   177  	_, ok := vschema.Keyspaces[ks].Vindexes["test_vindex"]
   178  	if ok {
   179  		t.Fatalf("test_vindex should not exist in original vschema")
   180  	}
   181  
   182  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
   183  	stmt := "alter vschema create vindex test_vindex using hash"
   184  	_, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   185  	require.NoError(t, err)
   186  
   187  	_, vindex := waitForVindex(t, ks, "test_vindex", vschemaUpdates, executor)
   188  	if vindex == nil || vindex.Type != "hash" {
   189  		t.Errorf("updated vschema did not contain test_vindex")
   190  	}
   191  
   192  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   193  	wantErr := "vindex test_vindex already exists in keyspace TestExecutor"
   194  	if err == nil || err.Error() != wantErr {
   195  		t.Errorf("create duplicate vindex: %v, want %s", err, wantErr)
   196  	}
   197  	select {
   198  	case <-vschemaUpdates:
   199  		t.Error("vschema should not be updated on error")
   200  	default:
   201  	}
   202  }
   203  
   204  func TestPlanExecutorDropVindexDDL(t *testing.T) {
   205  	vschemaacl.AuthorizedDDLUsers = "%"
   206  	defer func() {
   207  		vschemaacl.AuthorizedDDLUsers = ""
   208  	}()
   209  	executor, _, _, _ := createExecutorEnv()
   210  	ks := "TestExecutor"
   211  
   212  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4)
   213  	executor.serv.WatchSrvVSchema(context.Background(), "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
   214  		vschemaUpdates <- vschema
   215  		return true
   216  	})
   217  
   218  	vschema := <-vschemaUpdates
   219  	_, ok := vschema.Keyspaces[ks].Vindexes["test_vindex"]
   220  	if ok {
   221  		t.Fatalf("test_vindex should not exist in original vschema")
   222  	}
   223  
   224  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
   225  	stmt := "alter vschema drop vindex test_vindex"
   226  	_, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   227  	wantErr := "vindex test_vindex does not exists in keyspace TestExecutor"
   228  	if err == nil || err.Error() != wantErr {
   229  		t.Errorf("want error %v got %v", wantErr, err)
   230  	}
   231  
   232  	stmt = "alter vschema drop vindex TestExecutor.test_vindex"
   233  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   234  	wantErr = "vindex test_vindex does not exists in keyspace TestExecutor"
   235  	if err == nil || err.Error() != wantErr {
   236  		t.Errorf("want error %v got %v", wantErr, err)
   237  	}
   238  
   239  	// add one vindex that has never been used by the tables
   240  	stmt = "alter vschema create vindex test_vindex using hash"
   241  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   242  	require.NoError(t, err)
   243  
   244  	_, vindex := waitForVindex(t, ks, "test_vindex", vschemaUpdates, executor)
   245  	if vindex == nil || vindex.Type != "hash" {
   246  		t.Errorf("updated vschema did not contain test_vindex")
   247  	}
   248  
   249  	// drop an existing vindex that has never been used by the tables
   250  	stmt = "alter vschema drop vindex TestExecutor.test_vindex"
   251  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   252  	require.NoError(t, err)
   253  	vschema = <-vschemaUpdates
   254  	_, ok = vschema.Keyspaces[ks].Vindexes["test_vindex"]
   255  	if ok {
   256  		t.Fatalf("test_vindex should not exist after droping it")
   257  	}
   258  
   259  	// drop an existing vindex that is used by at least one table
   260  	stmt = "alter vschema drop vindex TestExecutor.keyspace_id"
   261  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   262  	wantErr = "can not drop vindex cause keyspace_id still defined on table ksid_table"
   263  	if err == nil || err.Error() != wantErr {
   264  		t.Errorf("drop vindex still defined: %v, want %s", err, wantErr)
   265  	}
   266  	select {
   267  	case <-vschemaUpdates:
   268  		t.Error("vschema should not be updated on error")
   269  	default:
   270  	}
   271  }
   272  
   273  func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) {
   274  	vschemaacl.AuthorizedDDLUsers = "%"
   275  	defer func() {
   276  		vschemaacl.AuthorizedDDLUsers = ""
   277  	}()
   278  	executor, sbc1, sbc2, sbclookup := createExecutorEnv()
   279  	ks := KsTestUnsharded
   280  
   281  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4)
   282  	executor.serv.WatchSrvVSchema(context.Background(), "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
   283  		vschemaUpdates <- vschema
   284  		return true
   285  	})
   286  
   287  	vschema := <-vschemaUpdates
   288  	_, ok := vschema.Keyspaces[ks].Tables["test_table"]
   289  	if ok {
   290  		t.Fatalf("test_table should not exist in original vschema")
   291  	}
   292  
   293  	vschemaTables := []string{}
   294  	for t := range vschema.Keyspaces[ks].Tables {
   295  		vschemaTables = append(vschemaTables, t)
   296  	}
   297  
   298  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
   299  	stmt := "alter vschema add table test_table"
   300  	_, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   301  	require.NoError(t, err)
   302  	_ = waitForVschemaTables(t, ks, append([]string{"test_table"}, vschemaTables...), executor)
   303  
   304  	stmt = "alter vschema add table test_table2"
   305  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   306  	require.NoError(t, err)
   307  	_ = waitForVschemaTables(t, ks, append([]string{"test_table", "test_table2"}, vschemaTables...), executor)
   308  
   309  	// Should fail adding a table on a sharded keyspace
   310  	session = NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
   311  	stmt = "alter vschema add table test_table"
   312  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   313  	wantErr := "add vschema table: unsupported on sharded keyspace TestExecutor"
   314  	if err == nil || err.Error() != wantErr {
   315  		t.Errorf("want error %v got %v", wantErr, err)
   316  	}
   317  
   318  	// No queries should have gone to any tablets
   319  	wantCount := []int64{0, 0, 0}
   320  	gotCount := []int64{
   321  		sbc1.ExecCount.Get(),
   322  		sbc2.ExecCount.Get(),
   323  		sbclookup.ExecCount.Get(),
   324  	}
   325  	if !reflect.DeepEqual(gotCount, wantCount) {
   326  		t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount)
   327  	}
   328  }
   329  
   330  func TestExecutorAddSequenceDDL(t *testing.T) {
   331  	vschemaacl.AuthorizedDDLUsers = "%"
   332  	defer func() {
   333  		vschemaacl.AuthorizedDDLUsers = ""
   334  	}()
   335  	executor, _, _, _ := createExecutorEnv()
   336  	ks := KsTestUnsharded
   337  
   338  	vschema := executor.vm.GetCurrentSrvVschema()
   339  
   340  	var vschemaTables []string
   341  	for t := range vschema.Keyspaces[ks].Tables {
   342  		vschemaTables = append(vschemaTables, t)
   343  	}
   344  
   345  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
   346  	stmt := "alter vschema add sequence test_seq"
   347  	_, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   348  	require.NoError(t, err)
   349  	_ = waitForVschemaTables(t, ks, append(vschemaTables, []string{"test_seq"}...), executor)
   350  	vschema = executor.vm.GetCurrentSrvVschema()
   351  	table := vschema.Keyspaces[ks].Tables["test_seq"]
   352  	wantType := "sequence"
   353  	if table.Type != wantType {
   354  		t.Errorf("want table type sequence got %v", table)
   355  	}
   356  
   357  	// Should fail adding a table on a sharded keyspace
   358  	ksSharded := "TestExecutor"
   359  	session = NewSafeSession(&vtgatepb.Session{TargetString: ksSharded})
   360  
   361  	stmt = "alter vschema add sequence sequence_table"
   362  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   363  
   364  	wantErr := "add sequence table: unsupported on sharded keyspace TestExecutor"
   365  	if err == nil || err.Error() != wantErr {
   366  		t.Errorf("want error %v got %v", wantErr, err)
   367  	}
   368  
   369  	// Should be able to add autoincrement to table in sharded keyspace
   370  	stmt = "alter vschema on test_table add vindex hash_index (id)"
   371  	if _, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil); err != nil {
   372  		t.Error(err)
   373  	}
   374  	time.Sleep(10 * time.Millisecond)
   375  
   376  	stmt = "alter vschema on test_table add auto_increment id using `db-name`.`test_seq`"
   377  	if _, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil); err != nil {
   378  		t.Error(err)
   379  	}
   380  	time.Sleep(10 * time.Millisecond)
   381  
   382  	wantAutoInc := &vschemapb.AutoIncrement{Column: "id", Sequence: "`db-name`.test_seq"}
   383  	gotAutoInc := executor.vm.GetCurrentSrvVschema().Keyspaces[ksSharded].Tables["test_table"].AutoIncrement
   384  
   385  	if !reflect.DeepEqual(wantAutoInc, gotAutoInc) {
   386  		t.Errorf("want autoinc %v, got autoinc %v", wantAutoInc, gotAutoInc)
   387  	}
   388  }
   389  
   390  func TestExecutorAddDropVindexDDL(t *testing.T) {
   391  	vschemaacl.AuthorizedDDLUsers = "%"
   392  	defer func() {
   393  		vschemaacl.AuthorizedDDLUsers = ""
   394  	}()
   395  	executor, sbc1, sbc2, sbclookup := createExecutorEnv() // nolint
   396  	ks := "TestExecutor"
   397  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
   398  	vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4)
   399  	executor.serv.WatchSrvVSchema(context.Background(), "aa", func(vschema *vschemapb.SrvVSchema, err error) bool {
   400  		vschemaUpdates <- vschema
   401  		return true
   402  	})
   403  
   404  	vschema := <-vschemaUpdates
   405  	_, ok := vschema.Keyspaces[ks].Vindexes["test_hash"]
   406  	require.False(t, ok, "test_hash should not exist in original vschema")
   407  
   408  	// Create a new vindex implicitly with the statement
   409  	stmt := "alter vschema on test add vindex test_hash (id) using hash "
   410  	_, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   411  	require.NoError(t, err)
   412  
   413  	_, vindex := waitForVindex(t, ks, "test_hash", vschemaUpdates, executor)
   414  	require.Equal(t, "hash", vindex.Type)
   415  
   416  	_ = waitForColVindexes(t, ks, "test", []string{"test_hash"}, executor)
   417  	qr, err := executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.test", nil)
   418  	require.NoError(t, err)
   419  	wantqr := &sqltypes.Result{
   420  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   421  		Rows: [][]sqltypes.Value{
   422  			buildVarCharRow("id", "test_hash", "hash", "", ""),
   423  		},
   424  	}
   425  	utils.MustMatch(t, wantqr, qr)
   426  
   427  	// Drop it
   428  	stmt = "alter vschema on test drop vindex test_hash"
   429  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   430  	require.NoError(t, err)
   431  
   432  	_, _ = waitForVindex(t, ks, "test_hash", vschemaUpdates, executor)
   433  	_ = waitForColVindexes(t, ks, "test", []string{}, executor)
   434  	_, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.test", nil)
   435  	require.EqualError(t, err, "VT05005: table 'test' does not exist in keyspace 'TestExecutor'")
   436  
   437  	// add it again using the same syntax
   438  	stmt = "alter vschema on test add vindex test_hash (id) using hash "
   439  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   440  	require.NoError(t, err)
   441  
   442  	_, vindex = waitForVindex(t, ks, "test_hash", vschemaUpdates, executor)
   443  	require.Equal(t, "hash", vindex.Type)
   444  
   445  	_ = waitForColVindexes(t, ks, "test", []string{"test_hash"}, executor)
   446  
   447  	qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.test", nil)
   448  	require.NoError(t, err)
   449  	wantqr = &sqltypes.Result{
   450  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   451  		Rows: [][]sqltypes.Value{
   452  			buildVarCharRow("id", "test_hash", "hash", "", ""),
   453  		},
   454  		RowsAffected: 0,
   455  	}
   456  	utils.MustMatch(t, wantqr, qr)
   457  
   458  	// add another
   459  	stmt = "alter vschema on test add vindex test_lookup (c1,c2) using lookup with owner=`test`, from=`c1,c2`, table=test_lookup, to=keyspace_id"
   460  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   461  	require.NoError(t, err)
   462  
   463  	vschema, vindex = waitForVindex(t, ks, "test_lookup", vschemaUpdates, executor)
   464  	require.Equal(t, "lookup", vindex.Type)
   465  
   466  	if table, ok := vschema.Keyspaces[ks].Tables["test"]; ok {
   467  		if len(table.ColumnVindexes) != 2 {
   468  			t.Fatalf("table vindexes want 1 got %d", len(table.ColumnVindexes))
   469  		}
   470  		if table.ColumnVindexes[1].Name != "test_lookup" {
   471  			t.Fatalf("table vindexes didn't contain test_lookup")
   472  		}
   473  	} else {
   474  		t.Fatalf("table test not defined in vschema")
   475  	}
   476  
   477  	qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.test", nil)
   478  	require.NoError(t, err)
   479  	wantqr = &sqltypes.Result{
   480  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   481  		Rows: [][]sqltypes.Value{
   482  			buildVarCharRow("id", "test_hash", "hash", "", ""),
   483  			buildVarCharRow("c1, c2", "test_lookup", "lookup", "from=c1,c2; table=test_lookup; to=keyspace_id", "test"),
   484  		},
   485  	}
   486  	utils.MustMatch(t, wantqr, qr)
   487  
   488  	stmt = "alter vschema on test add vindex test_hash_id2 (id2) using hash"
   489  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   490  	require.NoError(t, err)
   491  
   492  	vschema, vindex = waitForVindex(t, ks, "test_hash_id2", vschemaUpdates, executor)
   493  	require.Equal(t, "hash", vindex.Type)
   494  
   495  	if table, ok := vschema.Keyspaces[ks].Tables["test"]; ok {
   496  		if len(table.ColumnVindexes) != 3 {
   497  			t.Fatalf("table vindexes want 1 got %d", len(table.ColumnVindexes))
   498  		}
   499  		if table.ColumnVindexes[2].Name != "test_hash_id2" {
   500  			t.Fatalf("table vindexes didn't contain test_hash_id2")
   501  		}
   502  	} else {
   503  		t.Fatalf("table test not defined in vschema")
   504  	}
   505  
   506  	qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.test", nil)
   507  	require.NoError(t, err)
   508  	wantqr = &sqltypes.Result{
   509  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   510  		Rows: [][]sqltypes.Value{
   511  			buildVarCharRow("id", "test_hash", "hash", "", ""),
   512  			buildVarCharRow("c1, c2", "test_lookup", "lookup", "from=c1,c2; table=test_lookup; to=keyspace_id", "test"),
   513  			buildVarCharRow("id2", "test_hash_id2", "hash", "", ""),
   514  		},
   515  	}
   516  	utils.MustMatch(t, wantqr, qr)
   517  
   518  	// drop one
   519  	stmt = "alter vschema on test drop vindex test_lookup"
   520  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   521  	require.NoError(t, err)
   522  
   523  	// wait for up to 50ms for it to disappear
   524  	deadline := time.Now().Add(50 * time.Millisecond)
   525  	for {
   526  		qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.test", nil)
   527  		require.NoError(t, err)
   528  		wantqr = &sqltypes.Result{
   529  			Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   530  			Rows: [][]sqltypes.Value{
   531  				buildVarCharRow("id", "test_hash", "hash", "", ""),
   532  				buildVarCharRow("id2", "test_hash_id2", "hash", "", ""),
   533  			},
   534  		}
   535  		if reflect.DeepEqual(qr, wantqr) {
   536  			break
   537  		}
   538  
   539  		if time.Now().After(deadline) {
   540  			require.Fail(t, "timed out waiting for test_lookup vindex to be removed")
   541  		}
   542  		time.Sleep(1 * time.Millisecond)
   543  	}
   544  
   545  	// use the newly created vindex on a new table
   546  	stmt = "alter vschema on test2 add vindex test_hash (id)"
   547  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   548  	require.NoError(t, err)
   549  
   550  	vschema, vindex = waitForVindex(t, ks, "test_hash", vschemaUpdates, executor)
   551  	require.Equal(t, "hash", vindex.Type)
   552  
   553  	table, ok := vschema.Keyspaces[ks].Tables["test2"]
   554  	require.Truef(t, ok, "table test2 not defined in vschema")
   555  	require.Len(t, table.ColumnVindexes, 1)
   556  	require.Equal(t, "test_hash", table.ColumnVindexes[0].Name)
   557  
   558  	// create an identical vindex definition on a different table
   559  	stmt = "alter vschema on test2 add vindex test_lookup (c1,c2) using lookup with owner=`test`, from=`c1,c2`, table=test_lookup, to=keyspace_id"
   560  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   561  	require.NoError(t, err)
   562  
   563  	vschema, vindex = waitForVindex(t, ks, "test_lookup", vschemaUpdates, executor)
   564  	require.Equal(t, "lookup", vindex.Type)
   565  
   566  	table, ok = vschema.Keyspaces[ks].Tables["test2"]
   567  	require.Truef(t, ok, "table test2 not defined in vschema")
   568  	require.Len(t, table.ColumnVindexes, 2)
   569  	require.Equal(t, "test_lookup", table.ColumnVindexes[1].Name)
   570  
   571  	qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.test2", nil)
   572  	require.NoError(t, err)
   573  	wantqr = &sqltypes.Result{
   574  		Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   575  		Rows: [][]sqltypes.Value{
   576  			buildVarCharRow("id", "test_hash", "hash", "", ""),
   577  			buildVarCharRow("c1, c2", "test_lookup", "lookup", "from=c1,c2; table=test_lookup; to=keyspace_id", "test"),
   578  		},
   579  	}
   580  	utils.MustMatch(t, wantqr, qr)
   581  
   582  	// now make sure we can create another vindex that references a table with dashes (i.e. escaping is necessary)
   583  	stmt = "alter vschema on test2 add vindex test_lookup_fqn(c1,c2) using consistent_lookup_unique with owner=`test`, from=`c1,c2`, table=`test-keyspace`.`lookup-fqn`, to=`keyspace_id`"
   584  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   585  	require.NoError(t, err)
   586  
   587  	_, vindex = waitForVindex(t, ks, "test_lookup_fqn", vschemaUpdates, executor)
   588  	require.Equal(t, "consistent_lookup_unique", vindex.Type)
   589  	require.Equal(t, "test", vindex.Owner)
   590  	require.Equal(t, "c1,c2", vindex.Params["from"])
   591  	require.Equal(t, "`test-keyspace`.`lookup-fqn`", vindex.Params["table"])
   592  	require.Equal(t, "keyspace_id", vindex.Params["to"])
   593  
   594  	stmt = "alter vschema on test2 add vindex nonexistent (c1,c2)"
   595  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   596  	require.EqualError(t, err, "vindex nonexistent does not exist in keyspace TestExecutor")
   597  
   598  	stmt = "alter vschema on test2 add vindex test_hash (c1,c2) using lookup"
   599  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   600  	require.EqualError(t, err, "vindex test_hash defined with type hash not lookup")
   601  
   602  	stmt = "alter vschema on test2 add vindex test_lookup (c1,c2) using lookup with owner=xyz"
   603  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   604  	require.EqualError(t, err, "vindex test_lookup defined with owner test not xyz")
   605  
   606  	stmt = "alter vschema on test2 add vindex test_lookup (c1,c2) using lookup with owner=`test`, foo=bar"
   607  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   608  	require.EqualError(t, err, "vindex test_lookup defined with different parameters")
   609  
   610  	stmt = "alter vschema on nonexistent drop vindex test_lookup"
   611  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   612  	require.EqualError(t, err, "table TestExecutor.nonexistent not defined in vschema")
   613  
   614  	stmt = "alter vschema on nonexistent drop vindex test_lookup"
   615  	_, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "InvalidKeyspace"}), stmt, nil)
   616  	require.EqualError(t, err, "VT05003: unknown database 'InvalidKeyspace' in vschema")
   617  
   618  	stmt = "alter vschema on nowhere.nohow drop vindex test_lookup"
   619  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   620  	require.EqualError(t, err, "VT05003: unknown database 'nowhere' in vschema")
   621  
   622  	stmt = "alter vschema on test drop vindex test_lookup"
   623  	_, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil)
   624  	require.EqualError(t, err, "vindex test_lookup not defined in table TestExecutor.test")
   625  
   626  	// no queries should have gone to any tablets
   627  	wantCount := []int64{0, 0, 0}
   628  	gotCount := []int64{
   629  		sbc1.ExecCount.Get(),
   630  		sbc2.ExecCount.Get(),
   631  		sbclookup.ExecCount.Get(),
   632  	}
   633  	utils.MustMatch(t, wantCount, gotCount)
   634  }
   635  
   636  func TestPlanExecutorVindexDDLACL(t *testing.T) {
   637  	// t.Skip("not yet planned")
   638  	executor, _, _, _ := createExecutorEnv()
   639  	ks := "TestExecutor"
   640  	session := NewSafeSession(&vtgatepb.Session{TargetString: ks})
   641  
   642  	ctxRedUser := callerid.NewContext(context.Background(), &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "redUser"})
   643  	ctxBlueUser := callerid.NewContext(context.Background(), &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "blueUser"})
   644  
   645  	// test that by default no users can perform the operation
   646  	stmt := "alter vschema create vindex test_hash using hash"
   647  	_, err := executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil)
   648  	require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`)
   649  
   650  	_, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil)
   651  	require.EqualError(t, err, `User 'blueUser' is not authorized to perform vschema operations`)
   652  
   653  	// test when all users are enabled
   654  	vschemaacl.AuthorizedDDLUsers = "%"
   655  	vschemaacl.Init()
   656  	_, err = executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil)
   657  	if err != nil {
   658  		t.Errorf("unexpected error '%v'", err)
   659  	}
   660  	stmt = "alter vschema create vindex test_hash2 using hash"
   661  	_, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil)
   662  	if err != nil {
   663  		t.Errorf("unexpected error '%v'", err)
   664  	}
   665  
   666  	// test when only one user is enabled
   667  	vschemaacl.AuthorizedDDLUsers = "orangeUser, blueUser, greenUser"
   668  	vschemaacl.Init()
   669  	_, err = executor.Execute(ctxRedUser, "TestExecute", session, stmt, nil)
   670  	require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`)
   671  
   672  	stmt = "alter vschema create vindex test_hash3 using hash"
   673  	_, err = executor.Execute(ctxBlueUser, "TestExecute", session, stmt, nil)
   674  	if err != nil {
   675  		t.Errorf("unexpected error '%v'", err)
   676  	}
   677  
   678  	// restore the disallowed state
   679  	vschemaacl.AuthorizedDDLUsers = ""
   680  }