vitess.io/vitess@v0.16.2/go/vt/wrangler/materializer_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 wrangler
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sort"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/stretchr/testify/require"
    28  	"google.golang.org/protobuf/proto"
    29  
    30  	"vitess.io/vitess/go/sqltypes"
    31  	"vitess.io/vitess/go/test/utils"
    32  	"vitess.io/vitess/go/vt/logutil"
    33  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    34  	querypb "vitess.io/vitess/go/vt/proto/query"
    35  	tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata"
    36  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    37  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    38  	vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
    39  	"vitess.io/vitess/go/vt/topo/memorytopo"
    40  )
    41  
    42  const mzUpdateQuery = "update _vt.vreplication set state='Running' where db_name='vt_targetks' and workflow='workflow'"
    43  const mzSelectIDQuery = "select id from _vt.vreplication where db_name='vt_targetks' and workflow='workflow'"
    44  const mzSelectFrozenQuery = "select 1 from _vt.vreplication where db_name='vt_targetks' and message='FROZEN' and workflow_sub_type != 1"
    45  const mzCheckJournal = "/select val from _vt.resharding_journal where id="
    46  
    47  var defaultOnDDL = binlogdatapb.OnDDLAction_name[int32(binlogdatapb.OnDDLAction_IGNORE)]
    48  
    49  func TestMigrateTables(t *testing.T) {
    50  	ms := &vtctldatapb.MaterializeSettings{
    51  		Workflow:       "workflow",
    52  		SourceKeyspace: "sourceks",
    53  		TargetKeyspace: "targetks",
    54  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
    55  			TargetTable:      "t1",
    56  			SourceExpression: "select * from t1",
    57  		}},
    58  	}
    59  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
    60  	defer env.close()
    61  
    62  	env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{})
    63  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
    64  	env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{})
    65  	env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{})
    66  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
    67  
    68  	ctx := context.Background()
    69  	err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil)
    70  	require.NoError(t, err)
    71  	vschema, err := env.wr.ts.GetSrvVSchema(ctx, env.cell)
    72  	require.NoError(t, err)
    73  	got := fmt.Sprintf("%v", vschema)
    74  	want := []string{
    75  		`keyspaces:{key:"sourceks" value:{}} keyspaces:{key:"targetks" value:{tables:{key:"t1" value:{}}}}`,
    76  		`rules:{from_table:"t1" to_tables:"sourceks.t1"}`,
    77  		`rules:{from_table:"targetks.t1" to_tables:"sourceks.t1"}`,
    78  	}
    79  	for _, wantstr := range want {
    80  		require.Contains(t, got, wantstr)
    81  	}
    82  }
    83  
    84  func TestMissingTables(t *testing.T) {
    85  	ms := &vtctldatapb.MaterializeSettings{
    86  		Workflow:       "workflow",
    87  		SourceKeyspace: "sourceks",
    88  		TargetKeyspace: "targetks",
    89  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
    90  			TargetTable:      "t1",
    91  			SourceExpression: "select * from t1",
    92  		}, {
    93  			TargetTable:      "t2",
    94  			SourceExpression: "select * from t2",
    95  		}, {
    96  			TargetTable:      "t3",
    97  			SourceExpression: "select * from t3",
    98  		}},
    99  	}
   100  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   101  	defer env.close()
   102  
   103  	env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{})
   104  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
   105  	env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{})
   106  	env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{})
   107  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
   108  
   109  	ctx := context.Background()
   110  	err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1,tyt", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil)
   111  	require.EqualError(t, err, "table(s) not found in source keyspace sourceks: tyt")
   112  	err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1,tyt,t2,txt", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil)
   113  	require.EqualError(t, err, "table(s) not found in source keyspace sourceks: tyt,txt")
   114  	err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil)
   115  	require.NoError(t, err)
   116  }
   117  
   118  func TestMoveTablesAllAndExclude(t *testing.T) {
   119  	ms := &vtctldatapb.MaterializeSettings{
   120  		Workflow:       "workflow",
   121  		SourceKeyspace: "sourceks",
   122  		TargetKeyspace: "targetks",
   123  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
   124  			TargetTable:      "t1",
   125  			SourceExpression: "select * from t1",
   126  		}, {
   127  			TargetTable:      "t2",
   128  			SourceExpression: "select * from t2",
   129  		}, {
   130  			TargetTable:      "t3",
   131  			SourceExpression: "select * from t3",
   132  		}},
   133  	}
   134  
   135  	ctx := context.Background()
   136  	var err error
   137  
   138  	var targetTables = func(env *testMaterializerEnv) []string {
   139  		vschema, err := env.wr.ts.GetSrvVSchema(ctx, env.cell)
   140  		require.NoError(t, err)
   141  		var targetTables []string
   142  		for table := range vschema.Keyspaces["targetks"].Tables {
   143  			targetTables = append(targetTables, table)
   144  		}
   145  		sort.Strings(targetTables)
   146  		return targetTables
   147  	}
   148  	allTables := []string{"t1", "t2", "t3"}
   149  	sort.Strings(allTables)
   150  
   151  	type testCase struct {
   152  		allTables     bool
   153  		excludeTables string
   154  		want          []string
   155  	}
   156  	testCases := []*testCase{
   157  		{true, "", allTables},
   158  		{true, "t2,t3", []string{"t1"}},
   159  		{true, "t1", []string{"t2", "t3"}},
   160  	}
   161  	for _, tcase := range testCases {
   162  		t.Run("", func(t *testing.T) {
   163  			env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   164  			defer env.close()
   165  			env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{})
   166  			env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
   167  			env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{})
   168  			env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{})
   169  			env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
   170  			err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "", "", "", tcase.allTables, tcase.excludeTables, true, false, "", false, false, "", defaultOnDDL, nil)
   171  			require.NoError(t, err)
   172  			require.EqualValues(t, tcase.want, targetTables(env))
   173  		})
   174  
   175  	}
   176  
   177  }
   178  
   179  func TestMoveTablesStopFlags(t *testing.T) {
   180  	ms := &vtctldatapb.MaterializeSettings{
   181  		Workflow:       "workflow",
   182  		SourceKeyspace: "sourceks",
   183  		TargetKeyspace: "targetks",
   184  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
   185  			TargetTable:      "t1",
   186  			SourceExpression: "select * from t1",
   187  		}},
   188  	}
   189  
   190  	ctx := context.Background()
   191  	var err error
   192  	t.Run("StopStartedAndStopAfterCopyFlags", func(t *testing.T) {
   193  		env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   194  		defer env.close()
   195  		env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{})
   196  		env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
   197  		// insert expects flag stop_after_copy to be true
   198  		insert := `/insert into _vt.vreplication\(workflow, source, pos, max_tps, max_replication_lag, cell, tablet_types.*stop_after_copy:true.*`
   199  
   200  		env.tmc.expectVRQuery(200, insert, &sqltypes.Result{})
   201  		env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{})
   202  		// -auto_start=false is tested by NOT expecting the update query which sets state to RUNNING
   203  		err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "",
   204  			"", false, "", false, true, "", false, false, "", defaultOnDDL, nil)
   205  		require.NoError(t, err)
   206  		env.tmc.verifyQueries(t)
   207  	})
   208  }
   209  
   210  func TestMigrateVSchema(t *testing.T) {
   211  	ms := &vtctldatapb.MaterializeSettings{
   212  		Workflow:       "workflow",
   213  		SourceKeyspace: "sourceks",
   214  		TargetKeyspace: "targetks",
   215  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
   216  			TargetTable:      "t1",
   217  			SourceExpression: "select * from t1",
   218  		}},
   219  	}
   220  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   221  	defer env.close()
   222  
   223  	env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{})
   224  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
   225  	env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{})
   226  	env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{})
   227  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
   228  
   229  	ctx := context.Background()
   230  	err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", `{"t1":{}}`, "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil)
   231  	require.NoError(t, err)
   232  	vschema, err := env.wr.ts.GetSrvVSchema(ctx, env.cell)
   233  	require.NoError(t, err)
   234  	got := fmt.Sprintf("%v", vschema)
   235  	want := []string{`keyspaces:{key:"sourceks" value:{}}`,
   236  		`keyspaces:{key:"sourceks" value:{}} keyspaces:{key:"targetks" value:{tables:{key:"t1" value:{}}}}`,
   237  		`rules:{from_table:"t1" to_tables:"sourceks.t1"}`,
   238  		`rules:{from_table:"targetks.t1" to_tables:"sourceks.t1"}`,
   239  	}
   240  	for _, wantstr := range want {
   241  		require.Contains(t, got, wantstr)
   242  	}
   243  }
   244  
   245  func TestCreateLookupVindexFull(t *testing.T) {
   246  	ms := &vtctldatapb.MaterializeSettings{
   247  		Workflow:       "lkp_vdx",
   248  		SourceKeyspace: "sourceks",
   249  		TargetKeyspace: "targetks",
   250  	}
   251  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   252  	defer env.close()
   253  
   254  	specs := &vschemapb.Keyspace{
   255  		Vindexes: map[string]*vschemapb.Vindex{
   256  			"v": {
   257  				Type: "lookup_unique",
   258  				Params: map[string]string{
   259  					"table": "targetks.lkp",
   260  					"from":  "c1",
   261  					"to":    "c2",
   262  				},
   263  				Owner: "t1",
   264  			},
   265  		},
   266  		Tables: map[string]*vschemapb.Table{
   267  			"t1": {
   268  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   269  					Name:   "v",
   270  					Column: "col2",
   271  				}},
   272  			},
   273  		},
   274  	}
   275  	// Dummy sourceSchema
   276  	sourceSchema := "CREATE TABLE `t1` (\n" +
   277  		"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
   278  		"  `col2` int(11) DEFAULT NULL,\n" +
   279  		"  PRIMARY KEY (`id`)\n" +
   280  		") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1"
   281  
   282  	sourceVSchema := &vschemapb.Keyspace{
   283  		Sharded: true,
   284  		Vindexes: map[string]*vschemapb.Vindex{
   285  			"hash": {
   286  				Type: "hash",
   287  			},
   288  		},
   289  		Tables: map[string]*vschemapb.Table{
   290  			"t1": {
   291  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   292  					Name:   "hash",
   293  					Column: "col1",
   294  				}},
   295  			},
   296  		},
   297  	}
   298  	env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{
   299  		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{
   300  			Fields: []*querypb.Field{{
   301  				Name: "col1",
   302  				Type: querypb.Type_INT64,
   303  			}, {
   304  				Name: "col2",
   305  				Type: querypb.Type_INT64,
   306  			}},
   307  			Schema: sourceSchema,
   308  		}},
   309  	}
   310  	if err := env.topoServ.SaveVSchema(context.Background(), ms.TargetKeyspace, &vschemapb.Keyspace{}); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  	if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, sourceVSchema); err != nil {
   314  		t.Fatal(err)
   315  	}
   316  
   317  	env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{})
   318  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
   319  	env.tmc.expectVRQuery(200, "/CREATE TABLE `lkp`", &sqltypes.Result{})
   320  	env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{})
   321  	env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_targetks' and workflow='lkp_vdx'", &sqltypes.Result{})
   322  
   323  	ctx := context.Background()
   324  	err := env.wr.CreateLookupVindex(ctx, ms.SourceKeyspace, specs, "cell", "PRIMARY", false)
   325  	require.NoError(t, err)
   326  
   327  	wantvschema := &vschemapb.Keyspace{
   328  		Sharded: true,
   329  		Vindexes: map[string]*vschemapb.Vindex{
   330  			"hash": {
   331  				Type: "hash",
   332  			},
   333  			"v": {
   334  				Type: "lookup_unique",
   335  				Params: map[string]string{
   336  					"table":      "targetks.lkp",
   337  					"from":       "c1",
   338  					"to":         "c2",
   339  					"write_only": "true",
   340  				},
   341  				Owner: "t1",
   342  			},
   343  		},
   344  		Tables: map[string]*vschemapb.Table{
   345  			"t1": {
   346  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   347  					Name:   "hash",
   348  					Column: "col1",
   349  				}, {
   350  					Name:   "v",
   351  					Column: "col2",
   352  				}},
   353  			},
   354  		},
   355  	}
   356  	vschema, err := env.topoServ.GetVSchema(ctx, ms.SourceKeyspace)
   357  	require.NoError(t, err)
   358  	utils.MustMatch(t, wantvschema, vschema)
   359  
   360  	wantvschema = &vschemapb.Keyspace{
   361  		Tables: map[string]*vschemapb.Table{
   362  			"lkp": {},
   363  		},
   364  	}
   365  	vschema, err = env.topoServ.GetVSchema(ctx, ms.TargetKeyspace)
   366  	require.NoError(t, err)
   367  	utils.MustMatch(t, wantvschema, vschema)
   368  }
   369  
   370  func TestCreateLookupVindexCreateDDL(t *testing.T) {
   371  	ms := &vtctldatapb.MaterializeSettings{
   372  		SourceKeyspace: "sourceks",
   373  		TargetKeyspace: "targetks",
   374  	}
   375  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   376  	defer env.close()
   377  	vs := &vschemapb.Keyspace{
   378  		Sharded: true,
   379  		Vindexes: map[string]*vschemapb.Vindex{
   380  			"hash": {
   381  				Type: "hash",
   382  			},
   383  		},
   384  		Tables: map[string]*vschemapb.Table{
   385  			"t1": {
   386  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   387  					Column: "col1",
   388  					Name:   "hash",
   389  				}},
   390  			},
   391  		},
   392  	}
   393  	if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vs); err != nil {
   394  		t.Fatal(err)
   395  	}
   396  
   397  	testcases := []struct {
   398  		description  string
   399  		specs        *vschemapb.Keyspace
   400  		sourceSchema string
   401  		out          string
   402  		err          string
   403  	}{{
   404  		description: "unique lookup",
   405  		specs: &vschemapb.Keyspace{
   406  			Vindexes: map[string]*vschemapb.Vindex{
   407  				"v": {
   408  					Type: "lookup_unique",
   409  					Params: map[string]string{
   410  						"table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace),
   411  						"from":  "c1",
   412  						"to":    "c2",
   413  					},
   414  					Owner: "t1",
   415  				},
   416  			},
   417  			Tables: map[string]*vschemapb.Table{
   418  				"t1": {
   419  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   420  						Name:   "v",
   421  						Column: "col2",
   422  					}},
   423  				},
   424  			},
   425  		},
   426  		sourceSchema: "CREATE TABLE `t1` (\n" +
   427  			"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
   428  			"  `col2` int(11) DEFAULT NULL,\n" +
   429  			"  `col3` int(11) DEFAULT NULL,\n" +
   430  			"  PRIMARY KEY (`id`)\n" +
   431  			") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1",
   432  		out: "CREATE TABLE `lkp` (\n" +
   433  			"  `c1` int(11),\n" +
   434  			"  `c2` varbinary(128),\n" +
   435  			"  PRIMARY KEY (`c1`)\n" +
   436  			")",
   437  	}, {
   438  		description: "unique lookup, also pk",
   439  		specs: &vschemapb.Keyspace{
   440  			Vindexes: map[string]*vschemapb.Vindex{
   441  				"v": {
   442  					Type: "lookup_unique",
   443  					Params: map[string]string{
   444  						"table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace),
   445  						"from":  "c1",
   446  						"to":    "c2",
   447  					},
   448  					Owner: "t1",
   449  				},
   450  			},
   451  			Tables: map[string]*vschemapb.Table{
   452  				"t1": {
   453  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   454  						Name:   "v",
   455  						Column: "col2",
   456  					}},
   457  				},
   458  			},
   459  		},
   460  		sourceSchema: "CREATE TABLE `t1` (\n" +
   461  			"  `col2` int(11) NOT NULL AUTO_INCREMENT,\n" +
   462  			"  `col1` int(11) DEFAULT NULL,\n" +
   463  			"  `col4` int(11) DEFAULT NULL,\n" +
   464  			"  PRIMARY KEY (`id`)\n" +
   465  			") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1",
   466  		out: "CREATE TABLE `lkp` (\n" +
   467  			"  `c1` int(11) NOT NULL,\n" +
   468  			"  `c2` varbinary(128),\n" +
   469  			"  PRIMARY KEY (`c1`)\n" +
   470  			")",
   471  	}, {
   472  		description: "non-unique lookup, also pk",
   473  		specs: &vschemapb.Keyspace{
   474  			Vindexes: map[string]*vschemapb.Vindex{
   475  				"v": {
   476  					Type: "lookup",
   477  					Params: map[string]string{
   478  						"table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace),
   479  						"from":  "c1,c2",
   480  						"to":    "c3",
   481  					},
   482  					Owner: "t1",
   483  				},
   484  			},
   485  			Tables: map[string]*vschemapb.Table{
   486  				"t1": {
   487  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   488  						Name:    "v",
   489  						Columns: []string{"col2", "col1"},
   490  					}},
   491  				},
   492  			},
   493  		},
   494  		sourceSchema: "CREATE TABLE `t1` (\n" +
   495  			"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
   496  			"  `col2` int(11) NOT NULL,\n" +
   497  			"  `col3` int(11) DEFAULT NULL,\n" +
   498  			"  PRIMARY KEY (`id`)\n" +
   499  			") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1",
   500  		out: "CREATE TABLE `lkp` (\n" +
   501  			"  `c1` int(11) NOT NULL,\n" +
   502  			"  `c2` int(11) NOT NULL,\n" +
   503  			"  `c3` varbinary(128),\n" +
   504  			"  PRIMARY KEY (`c1`, `c2`)\n" +
   505  			")",
   506  	}, {
   507  		description: "column missing",
   508  		specs: &vschemapb.Keyspace{
   509  			Vindexes: map[string]*vschemapb.Vindex{
   510  				"v": {
   511  					Type: "lookup_unique",
   512  					Params: map[string]string{
   513  						"table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace),
   514  						"from":  "c1",
   515  						"to":    "c2",
   516  					},
   517  					Owner: "t1",
   518  				},
   519  			},
   520  			Tables: map[string]*vschemapb.Table{
   521  				"t1": {
   522  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   523  						Name:   "v",
   524  						Column: "nocol",
   525  					}},
   526  				},
   527  			},
   528  		},
   529  		sourceSchema: "CREATE TABLE `t1` (\n" +
   530  			"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
   531  			"  `col2` int(11) NOT NULL,\n" +
   532  			"  `col3` int(11) DEFAULT NULL,\n" +
   533  			"  PRIMARY KEY (`id`)\n" +
   534  			") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1",
   535  		err: "column nocol not found in schema",
   536  	}, {
   537  		description: "no table in schema",
   538  		specs: &vschemapb.Keyspace{
   539  			Vindexes: map[string]*vschemapb.Vindex{
   540  				"v": {
   541  					Type: "lookup_unique",
   542  					Params: map[string]string{
   543  						"table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace),
   544  						"from":  "c1",
   545  						"to":    "c2",
   546  					},
   547  					Owner: "t1",
   548  				},
   549  			},
   550  			Tables: map[string]*vschemapb.Table{
   551  				"t1": {
   552  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   553  						Name:   "v",
   554  						Column: "nocol",
   555  					}},
   556  				},
   557  			},
   558  		},
   559  		sourceSchema: "",
   560  		err:          "unexpected number of tables returned from schema",
   561  	}}
   562  	for _, tcase := range testcases {
   563  		if tcase.sourceSchema != "" {
   564  			env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{
   565  				TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{
   566  					Schema: tcase.sourceSchema,
   567  				}},
   568  			}
   569  		} else {
   570  			delete(env.tmc.schema, ms.SourceKeyspace+".t1")
   571  		}
   572  
   573  		outms, _, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, tcase.specs, false)
   574  		if tcase.err != "" {
   575  			if err == nil || !strings.Contains(err.Error(), tcase.err) {
   576  				t.Errorf("prepareCreateLookup(%s) err: %v, must contain %v", tcase.description, err, tcase.err)
   577  			}
   578  			continue
   579  		}
   580  		require.NoError(t, err)
   581  		want := strings.Split(tcase.out, "\n")
   582  		got := strings.Split(outms.TableSettings[0].CreateDdl, "\n")
   583  		require.Equal(t, want, got, tcase.description)
   584  	}
   585  }
   586  
   587  func TestCreateLookupVindexSourceVSchema(t *testing.T) {
   588  	ms := &vtctldatapb.MaterializeSettings{
   589  		SourceKeyspace: "sourceks",
   590  		TargetKeyspace: "targetks",
   591  	}
   592  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   593  	defer env.close()
   594  
   595  	specs := &vschemapb.Keyspace{
   596  		Vindexes: map[string]*vschemapb.Vindex{
   597  			"v": {
   598  				Type: "lookup_unique",
   599  				Params: map[string]string{
   600  					"table": "targetks.lkp",
   601  					"from":  "c1",
   602  					"to":    "c2",
   603  				},
   604  				Owner: "t1",
   605  			},
   606  		},
   607  		Tables: map[string]*vschemapb.Table{
   608  			"t1": {
   609  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   610  					Name:   "v",
   611  					Column: "col2",
   612  				}},
   613  			},
   614  		},
   615  	}
   616  	// Dummy sourceSchema
   617  	sourceSchema := "CREATE TABLE `t1` (\n" +
   618  		"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
   619  		"  `col2` int(11) DEFAULT NULL,\n" +
   620  		"  PRIMARY KEY (`id`)\n" +
   621  		") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1"
   622  
   623  	testcases := []struct {
   624  		description   string
   625  		sourceVSchema *vschemapb.Keyspace
   626  		out           *vschemapb.Keyspace
   627  	}{{
   628  		description: "source vschema has no prior info",
   629  		sourceVSchema: &vschemapb.Keyspace{
   630  			Sharded: true,
   631  			Vindexes: map[string]*vschemapb.Vindex{
   632  				"hash": {
   633  					Type: "hash",
   634  				},
   635  			},
   636  			Tables: map[string]*vschemapb.Table{
   637  				"t1": {
   638  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   639  						Name:   "hash",
   640  						Column: "col1",
   641  					}},
   642  				},
   643  			},
   644  		},
   645  		out: &vschemapb.Keyspace{
   646  			Sharded: true,
   647  			Vindexes: map[string]*vschemapb.Vindex{
   648  				"hash": {
   649  					Type: "hash",
   650  				},
   651  				"v": {
   652  					Type: "lookup_unique",
   653  					Params: map[string]string{
   654  						"table":      "targetks.lkp",
   655  						"from":       "c1",
   656  						"to":         "c2",
   657  						"write_only": "true",
   658  					},
   659  					Owner: "t1",
   660  				},
   661  			},
   662  			Tables: map[string]*vschemapb.Table{
   663  				"t1": {
   664  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   665  						Name:   "hash",
   666  						Column: "col1",
   667  					}, {
   668  						Name:   "v",
   669  						Column: "col2",
   670  					}},
   671  				},
   672  			},
   673  		},
   674  	}, {
   675  		description: "source vschema has the lookup vindex",
   676  		sourceVSchema: &vschemapb.Keyspace{
   677  			Sharded: true,
   678  			Vindexes: map[string]*vschemapb.Vindex{
   679  				"hash": {
   680  					Type: "hash",
   681  				},
   682  				"v": {
   683  					Type: "lookup_unique",
   684  					Params: map[string]string{
   685  						"table":      "targetks.lkp",
   686  						"from":       "c1",
   687  						"to":         "c2",
   688  						"write_only": "true",
   689  					},
   690  					Owner: "t1",
   691  				},
   692  			},
   693  			Tables: map[string]*vschemapb.Table{
   694  				"t1": {
   695  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   696  						Name:   "hash",
   697  						Column: "col1",
   698  					}},
   699  				},
   700  			},
   701  		},
   702  		out: &vschemapb.Keyspace{
   703  			Sharded: true,
   704  			Vindexes: map[string]*vschemapb.Vindex{
   705  				"hash": {
   706  					Type: "hash",
   707  				},
   708  				"v": {
   709  					Type: "lookup_unique",
   710  					Params: map[string]string{
   711  						"table":      "targetks.lkp",
   712  						"from":       "c1",
   713  						"to":         "c2",
   714  						"write_only": "true",
   715  					},
   716  					Owner: "t1",
   717  				},
   718  			},
   719  			Tables: map[string]*vschemapb.Table{
   720  				"t1": {
   721  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   722  						Name:   "hash",
   723  						Column: "col1",
   724  					}, {
   725  						Name:   "v",
   726  						Column: "col2",
   727  					}},
   728  				},
   729  			},
   730  		},
   731  	}, {
   732  		description: "source vschema table has a different vindex on same column",
   733  		sourceVSchema: &vschemapb.Keyspace{
   734  			Sharded: true,
   735  			Vindexes: map[string]*vschemapb.Vindex{
   736  				"hash": {
   737  					Type: "hash",
   738  				},
   739  				"v": {
   740  					Type: "lookup_unique",
   741  					Params: map[string]string{
   742  						"table":      "targetks.lkp",
   743  						"from":       "c1",
   744  						"to":         "c2",
   745  						"write_only": "true",
   746  					},
   747  					Owner: "t1",
   748  				},
   749  			},
   750  			Tables: map[string]*vschemapb.Table{
   751  				"t1": {
   752  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   753  						Name:   "hash",
   754  						Column: "col1",
   755  					}, {
   756  						Name:   "hash",
   757  						Column: "col2",
   758  					}},
   759  				},
   760  			},
   761  		},
   762  		out: &vschemapb.Keyspace{
   763  			Sharded: true,
   764  			Vindexes: map[string]*vschemapb.Vindex{
   765  				"hash": {
   766  					Type: "hash",
   767  				},
   768  				"v": {
   769  					Type: "lookup_unique",
   770  					Params: map[string]string{
   771  						"table":      "targetks.lkp",
   772  						"from":       "c1",
   773  						"to":         "c2",
   774  						"write_only": "true",
   775  					},
   776  					Owner: "t1",
   777  				},
   778  			},
   779  			Tables: map[string]*vschemapb.Table{
   780  				"t1": {
   781  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   782  						Name:   "hash",
   783  						Column: "col1",
   784  					}, {
   785  						Name:   "hash",
   786  						Column: "col2",
   787  					}, {
   788  						Name:   "v",
   789  						Column: "col2",
   790  					}},
   791  				},
   792  			},
   793  		},
   794  	}}
   795  	for _, tcase := range testcases {
   796  		env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{
   797  			TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{
   798  				Fields: []*querypb.Field{{
   799  					Name: "col1",
   800  					Type: querypb.Type_INT64,
   801  				}, {
   802  					Name: "col2",
   803  					Type: querypb.Type_INT64,
   804  				}},
   805  				Schema: sourceSchema,
   806  			}},
   807  		}
   808  		if err := env.topoServ.SaveVSchema(context.Background(), ms.TargetKeyspace, &vschemapb.Keyspace{}); err != nil {
   809  			t.Fatal(err)
   810  		}
   811  		if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, tcase.sourceVSchema); err != nil {
   812  			t.Fatal(err)
   813  		}
   814  
   815  		_, got, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false)
   816  		require.NoError(t, err)
   817  		if !proto.Equal(got, tcase.out) {
   818  			t.Errorf("%s: got:\n%v, want\n%v", tcase.description, got, tcase.out)
   819  		}
   820  	}
   821  }
   822  
   823  func TestCreateLookupVindexTargetVSchema(t *testing.T) {
   824  	ms := &vtctldatapb.MaterializeSettings{
   825  		SourceKeyspace: "sourceks",
   826  		TargetKeyspace: "targetks",
   827  	}
   828  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
   829  	defer env.close()
   830  	sourcevs := &vschemapb.Keyspace{
   831  		Sharded: true,
   832  		Vindexes: map[string]*vschemapb.Vindex{
   833  			"hash": {
   834  				Type: "hash",
   835  			},
   836  		},
   837  		Tables: map[string]*vschemapb.Table{
   838  			"t1": {
   839  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   840  					Column: "col1",
   841  					Name:   "hash",
   842  				}},
   843  			},
   844  		},
   845  	}
   846  	if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, sourcevs); err != nil {
   847  		t.Fatal(err)
   848  	}
   849  
   850  	// withTable is a target vschema with a pre-existing table.
   851  	withTable := &vschemapb.Keyspace{
   852  		Sharded: true,
   853  		Vindexes: map[string]*vschemapb.Vindex{
   854  			"hash": {
   855  				Type: "hash",
   856  			},
   857  		},
   858  		Tables: map[string]*vschemapb.Table{
   859  			"t2": {
   860  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   861  					Column: "c1",
   862  					Name:   "hash",
   863  				}},
   864  			},
   865  		},
   866  	}
   867  
   868  	specs := &vschemapb.Keyspace{
   869  		Vindexes: map[string]*vschemapb.Vindex{
   870  			"v": {
   871  				Type: "lookup_unique",
   872  				Params: map[string]string{
   873  					"table": "will be set by the test case",
   874  					"from":  "c1",
   875  					"to":    "c2",
   876  				},
   877  				Owner: "t1",
   878  			},
   879  		},
   880  		Tables: map[string]*vschemapb.Table{
   881  			"t1": {
   882  				ColumnVindexes: []*vschemapb.ColumnVindex{{
   883  					Name:   "v",
   884  					Column: "col2",
   885  				}},
   886  			},
   887  		},
   888  	}
   889  	// Dummy sourceSchema
   890  	sourceSchema := "CREATE TABLE `t1` (\n" +
   891  		"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
   892  		"  `col2` int(11) DEFAULT NULL,\n" +
   893  		"  PRIMARY KEY (`id`)\n" +
   894  		") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1"
   895  
   896  	testcases := []struct {
   897  		description     string
   898  		targetTable     string
   899  		sourceFieldType querypb.Type
   900  		targetVSchema   *vschemapb.Keyspace
   901  		out             *vschemapb.Keyspace
   902  		err             string
   903  	}{{
   904  		description:     "sharded, int64, empty target",
   905  		targetTable:     "lkp",
   906  		sourceFieldType: querypb.Type_INT64,
   907  		targetVSchema:   &vschemapb.Keyspace{Sharded: true},
   908  		out: &vschemapb.Keyspace{
   909  			Sharded: true,
   910  			Vindexes: map[string]*vschemapb.Vindex{
   911  				"hash": {
   912  					Type: "hash",
   913  				},
   914  			},
   915  			Tables: map[string]*vschemapb.Table{
   916  				"lkp": {
   917  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   918  						Column: "c1",
   919  						Name:   "hash",
   920  					}},
   921  				},
   922  			},
   923  		},
   924  	}, {
   925  		description:     "sharded, varchar, empty target",
   926  		targetTable:     "lkp",
   927  		sourceFieldType: querypb.Type_VARCHAR,
   928  		targetVSchema:   &vschemapb.Keyspace{Sharded: true},
   929  		out: &vschemapb.Keyspace{
   930  			Sharded: true,
   931  			Vindexes: map[string]*vschemapb.Vindex{
   932  				"unicode_loose_md5": {
   933  					Type: "unicode_loose_md5",
   934  				},
   935  			},
   936  			Tables: map[string]*vschemapb.Table{
   937  				"lkp": {
   938  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   939  						Column: "c1",
   940  						Name:   "unicode_loose_md5",
   941  					}},
   942  				},
   943  			},
   944  		},
   945  	}, {
   946  		description:     "sharded, int64, good vindex",
   947  		targetTable:     "lkp",
   948  		sourceFieldType: querypb.Type_INT64,
   949  		targetVSchema: &vschemapb.Keyspace{
   950  			Sharded: true,
   951  			Vindexes: map[string]*vschemapb.Vindex{
   952  				// Create a misleading vindex name.
   953  				"hash": {
   954  					Type: "hash",
   955  				},
   956  			},
   957  		},
   958  		out: &vschemapb.Keyspace{
   959  			Sharded: true,
   960  			Vindexes: map[string]*vschemapb.Vindex{
   961  				"hash": {
   962  					Type: "hash",
   963  				},
   964  			},
   965  			Tables: map[string]*vschemapb.Table{
   966  				"lkp": {
   967  					ColumnVindexes: []*vschemapb.ColumnVindex{{
   968  						Column: "c1",
   969  						Name:   "hash",
   970  					}},
   971  				},
   972  			},
   973  		},
   974  	}, {
   975  		description:     "sharded, int64, bad vindex",
   976  		targetTable:     "lkp",
   977  		sourceFieldType: querypb.Type_INT64,
   978  		targetVSchema: &vschemapb.Keyspace{
   979  			Sharded: true,
   980  			Vindexes: map[string]*vschemapb.Vindex{
   981  				// Create a misleading vindex name.
   982  				"hash": {
   983  					Type: "unicode_loose_md5",
   984  				},
   985  			},
   986  		},
   987  		err: "a conflicting vindex named hash already exists in the target vschema",
   988  	}, {
   989  		description:     "sharded, int64, good table",
   990  		targetTable:     "t2",
   991  		sourceFieldType: querypb.Type_INT64,
   992  		targetVSchema:   withTable,
   993  		out: &vschemapb.Keyspace{
   994  			Sharded: true,
   995  			Vindexes: map[string]*vschemapb.Vindex{
   996  				"hash": {
   997  					Type: "hash",
   998  				},
   999  			},
  1000  			Tables: map[string]*vschemapb.Table{
  1001  				"t2": {
  1002  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1003  						Column: "c1",
  1004  						Name:   "hash",
  1005  					}},
  1006  				},
  1007  			},
  1008  		},
  1009  	}, {
  1010  		description:     "sharded, int64, table mismatch",
  1011  		targetTable:     "t2",
  1012  		sourceFieldType: querypb.Type_VARCHAR,
  1013  		targetVSchema:   withTable,
  1014  		err:             "a conflicting table named t2 already exists in the target vschema",
  1015  	}, {
  1016  		description:     "unsharded",
  1017  		targetTable:     "lkp",
  1018  		sourceFieldType: querypb.Type_INT64,
  1019  		targetVSchema:   &vschemapb.Keyspace{},
  1020  		out: &vschemapb.Keyspace{
  1021  			Vindexes: map[string]*vschemapb.Vindex{},
  1022  			Tables: map[string]*vschemapb.Table{
  1023  				"lkp": {},
  1024  			},
  1025  		},
  1026  	}, {
  1027  		description:     "invalid column type",
  1028  		targetTable:     "lkp",
  1029  		sourceFieldType: querypb.Type_SET,
  1030  		targetVSchema:   &vschemapb.Keyspace{Sharded: true},
  1031  		err:             "type SET is not recommended for a vindex",
  1032  	}}
  1033  	for _, tcase := range testcases {
  1034  		env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{
  1035  			TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{
  1036  				Fields: []*querypb.Field{{
  1037  					Name: "col2",
  1038  					Type: tcase.sourceFieldType,
  1039  				}},
  1040  				Schema: sourceSchema,
  1041  			}},
  1042  		}
  1043  		specs.Vindexes["v"].Params["table"] = fmt.Sprintf("%s.%s", ms.TargetKeyspace, tcase.targetTable)
  1044  		if err := env.topoServ.SaveVSchema(context.Background(), ms.TargetKeyspace, tcase.targetVSchema); err != nil {
  1045  			t.Fatal(err)
  1046  		}
  1047  
  1048  		_, _, got, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false)
  1049  		if tcase.err != "" {
  1050  			if err == nil || !strings.Contains(err.Error(), tcase.err) {
  1051  				t.Errorf("prepareCreateLookup(%s) err: %v, must contain %v", tcase.description, err, tcase.err)
  1052  			}
  1053  			continue
  1054  		}
  1055  		require.NoError(t, err)
  1056  		utils.MustMatch(t, tcase.out, got, tcase.description)
  1057  	}
  1058  }
  1059  
  1060  func TestCreateLookupVindexSameKeyspace(t *testing.T) {
  1061  	ms := &vtctldatapb.MaterializeSettings{
  1062  		SourceKeyspace: "ks",
  1063  		TargetKeyspace: "ks",
  1064  	}
  1065  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  1066  	defer env.close()
  1067  
  1068  	specs := &vschemapb.Keyspace{
  1069  		Vindexes: map[string]*vschemapb.Vindex{
  1070  			"v": {
  1071  				Type: "lookup_unique",
  1072  				Params: map[string]string{
  1073  					"table": "ks.lkp",
  1074  					"from":  "c1",
  1075  					"to":    "c2",
  1076  				},
  1077  				Owner: "t1",
  1078  			},
  1079  		},
  1080  		Tables: map[string]*vschemapb.Table{
  1081  			"t1": {
  1082  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1083  					Name:   "v",
  1084  					Column: "col2",
  1085  				}},
  1086  			},
  1087  		},
  1088  	}
  1089  	// Dummy sourceSchema
  1090  	sourceSchema := "CREATE TABLE `t1` (\n" +
  1091  		"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
  1092  		"  `col2` int(11) DEFAULT NULL,\n" +
  1093  		"  PRIMARY KEY (`id`)\n" +
  1094  		") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1"
  1095  
  1096  	vschema := &vschemapb.Keyspace{
  1097  		Sharded: true,
  1098  		Vindexes: map[string]*vschemapb.Vindex{
  1099  			"hash": {
  1100  				Type: "hash",
  1101  			},
  1102  		},
  1103  		Tables: map[string]*vschemapb.Table{
  1104  			"t1": {
  1105  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1106  					Name:   "hash",
  1107  					Column: "col1",
  1108  				}},
  1109  			},
  1110  		},
  1111  	}
  1112  	want := &vschemapb.Keyspace{
  1113  		Sharded: true,
  1114  		Vindexes: map[string]*vschemapb.Vindex{
  1115  			"hash": {
  1116  				Type: "hash",
  1117  			},
  1118  			"v": {
  1119  				Type: "lookup_unique",
  1120  				Params: map[string]string{
  1121  					"table":      "ks.lkp",
  1122  					"from":       "c1",
  1123  					"to":         "c2",
  1124  					"write_only": "true",
  1125  				},
  1126  				Owner: "t1",
  1127  			},
  1128  		},
  1129  		Tables: map[string]*vschemapb.Table{
  1130  			"t1": {
  1131  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1132  					Name:   "hash",
  1133  					Column: "col1",
  1134  				}, {
  1135  					Name:   "v",
  1136  					Column: "col2",
  1137  				}},
  1138  			},
  1139  			"lkp": {
  1140  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1141  					Column: "c1",
  1142  					Name:   "hash",
  1143  				}},
  1144  			},
  1145  		},
  1146  	}
  1147  	env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{
  1148  		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{
  1149  			Fields: []*querypb.Field{{
  1150  				Name: "col1",
  1151  				Type: querypb.Type_INT64,
  1152  			}, {
  1153  				Name: "col2",
  1154  				Type: querypb.Type_INT64,
  1155  			}},
  1156  			Schema: sourceSchema,
  1157  		}},
  1158  	}
  1159  	if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vschema); err != nil {
  1160  		t.Fatal(err)
  1161  	}
  1162  
  1163  	_, got, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false)
  1164  	require.NoError(t, err)
  1165  	if !proto.Equal(got, want) {
  1166  		t.Errorf("same keyspace: got:\n%v, want\n%v", got, want)
  1167  	}
  1168  }
  1169  func TestCreateCustomizedVindex(t *testing.T) {
  1170  	ms := &vtctldatapb.MaterializeSettings{
  1171  		SourceKeyspace: "ks",
  1172  		TargetKeyspace: "ks",
  1173  	}
  1174  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  1175  	defer env.close()
  1176  
  1177  	specs := &vschemapb.Keyspace{
  1178  		Vindexes: map[string]*vschemapb.Vindex{
  1179  			"v": {
  1180  				Type: "lookup_unique",
  1181  				Params: map[string]string{
  1182  					"table":     "ks.lkp",
  1183  					"from":      "c1",
  1184  					"to":        "col2",
  1185  					"data_type": "bigint(20)",
  1186  				},
  1187  				Owner: "t1",
  1188  			},
  1189  		},
  1190  		Tables: map[string]*vschemapb.Table{
  1191  			"t1": {
  1192  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1193  					Name:   "v",
  1194  					Column: "col2",
  1195  				}},
  1196  			},
  1197  		},
  1198  	}
  1199  	// Dummy sourceSchema
  1200  	sourceSchema := "CREATE TABLE `t1` (\n" +
  1201  		"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
  1202  		"  `col2` int(11) DEFAULT NULL,\n" +
  1203  		"  PRIMARY KEY (`id`)\n" +
  1204  		") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1"
  1205  
  1206  	vschema := &vschemapb.Keyspace{
  1207  		Sharded: true,
  1208  		Vindexes: map[string]*vschemapb.Vindex{
  1209  			"hash": {
  1210  				Type: "hash",
  1211  			},
  1212  		},
  1213  		Tables: map[string]*vschemapb.Table{
  1214  			"t1": {
  1215  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1216  					Name:   "hash",
  1217  					Column: "col1",
  1218  				}},
  1219  			},
  1220  		},
  1221  	}
  1222  	want := &vschemapb.Keyspace{
  1223  		Sharded: true,
  1224  		Vindexes: map[string]*vschemapb.Vindex{
  1225  			"hash": {
  1226  				Type: "hash",
  1227  			},
  1228  			"v": {
  1229  				Type: "lookup_unique",
  1230  				Params: map[string]string{
  1231  					"table":      "ks.lkp",
  1232  					"from":       "c1",
  1233  					"to":         "col2",
  1234  					"data_type":  "bigint(20)",
  1235  					"write_only": "true",
  1236  				},
  1237  				Owner: "t1",
  1238  			},
  1239  		},
  1240  		Tables: map[string]*vschemapb.Table{
  1241  			"t1": {
  1242  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1243  					Name:   "hash",
  1244  					Column: "col1",
  1245  				}, {
  1246  					Name:   "v",
  1247  					Column: "col2",
  1248  				}},
  1249  			},
  1250  			"lkp": {
  1251  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1252  					Column: "c1",
  1253  					Name:   "hash",
  1254  				}},
  1255  			},
  1256  		},
  1257  	}
  1258  	env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{
  1259  		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{
  1260  			Fields: []*querypb.Field{{
  1261  				Name: "col1",
  1262  				Type: querypb.Type_INT64,
  1263  			}, {
  1264  				Name: "col2",
  1265  				Type: querypb.Type_INT64,
  1266  			}},
  1267  			Schema: sourceSchema,
  1268  		}},
  1269  	}
  1270  	if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vschema); err != nil {
  1271  		t.Fatal(err)
  1272  	}
  1273  
  1274  	_, got, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false)
  1275  	require.NoError(t, err)
  1276  	if !proto.Equal(got, want) {
  1277  		t.Errorf("customize create lookup error same: got:\n%v, want\n%v", got, want)
  1278  	}
  1279  }
  1280  
  1281  func TestStopAfterCopyFlag(t *testing.T) {
  1282  	ms := &vtctldatapb.MaterializeSettings{
  1283  		SourceKeyspace: "ks",
  1284  		TargetKeyspace: "ks",
  1285  	}
  1286  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  1287  	defer env.close()
  1288  	specs := &vschemapb.Keyspace{
  1289  		Vindexes: map[string]*vschemapb.Vindex{
  1290  			"v": {
  1291  				Type: "lookup_unique",
  1292  				Params: map[string]string{
  1293  					"table": "ks.lkp",
  1294  					"from":  "c1",
  1295  					"to":    "col2",
  1296  				},
  1297  				Owner: "t1",
  1298  			},
  1299  		},
  1300  		Tables: map[string]*vschemapb.Table{
  1301  			"t1": {
  1302  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1303  					Name:   "v",
  1304  					Column: "col2",
  1305  				}},
  1306  			},
  1307  		},
  1308  	}
  1309  	// Dummy sourceSchema
  1310  	sourceSchema := "CREATE TABLE `t1` (\n" +
  1311  		"  `col1` int(11) NOT NULL AUTO_INCREMENT,\n" +
  1312  		"  `col2` int(11) DEFAULT NULL,\n" +
  1313  		"  PRIMARY KEY (`id`)\n" +
  1314  		") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1"
  1315  
  1316  	vschema := &vschemapb.Keyspace{
  1317  		Sharded: true,
  1318  		Vindexes: map[string]*vschemapb.Vindex{
  1319  			"hash": {
  1320  				Type: "hash",
  1321  			},
  1322  		},
  1323  		Tables: map[string]*vschemapb.Table{
  1324  			"t1": {
  1325  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1326  					Name:   "hash",
  1327  					Column: "col1",
  1328  				}},
  1329  			},
  1330  		},
  1331  	}
  1332  	env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{
  1333  		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{
  1334  			Fields: []*querypb.Field{{
  1335  				Name: "col1",
  1336  				Type: querypb.Type_INT64,
  1337  			}, {
  1338  				Name: "col2",
  1339  				Type: querypb.Type_INT64,
  1340  			}},
  1341  			Schema: sourceSchema,
  1342  		}},
  1343  	}
  1344  	if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vschema); err != nil {
  1345  		t.Fatal(err)
  1346  	}
  1347  
  1348  	ms1, _, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false)
  1349  	require.NoError(t, err)
  1350  	require.Equal(t, ms1.StopAfterCopy, true)
  1351  
  1352  	ms2, _, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, true)
  1353  	require.NoError(t, err)
  1354  	require.Equal(t, ms2.StopAfterCopy, false)
  1355  }
  1356  
  1357  func TestCreateLookupVindexFailures(t *testing.T) {
  1358  	topoServ := memorytopo.NewServer("cell")
  1359  	wr := New(logutil.NewConsoleLogger(), topoServ, nil)
  1360  
  1361  	unique := map[string]*vschemapb.Vindex{
  1362  		"v": {
  1363  			Type: "lookup_unique",
  1364  			Params: map[string]string{
  1365  				"table": "targetks.t",
  1366  				"from":  "c1",
  1367  				"to":    "c2",
  1368  			},
  1369  		},
  1370  	}
  1371  
  1372  	vs := &vschemapb.Keyspace{
  1373  		Sharded: true,
  1374  		Vindexes: map[string]*vschemapb.Vindex{
  1375  			"other": {
  1376  				Type: "hash",
  1377  			},
  1378  			"v": {
  1379  				Type: "lookup_unique",
  1380  				Params: map[string]string{
  1381  					"table":      "targetks.t",
  1382  					"from":       "c1",
  1383  					"to":         "c2",
  1384  					"write_only": "true",
  1385  				},
  1386  			},
  1387  		},
  1388  		Tables: map[string]*vschemapb.Table{
  1389  			"t1": {
  1390  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1391  					Column: "c1",
  1392  					Name:   "v",
  1393  				}},
  1394  			},
  1395  		},
  1396  	}
  1397  	if err := topoServ.SaveVSchema(context.Background(), "sourceks", vs); err != nil {
  1398  		t.Fatal(err)
  1399  	}
  1400  	if err := topoServ.SaveVSchema(context.Background(), "targetks", &vschemapb.Keyspace{}); err != nil {
  1401  		t.Fatal(err)
  1402  	}
  1403  
  1404  	testcases := []struct {
  1405  		description string
  1406  		input       *vschemapb.Keyspace
  1407  		err         string
  1408  	}{{
  1409  		description: "dup vindex",
  1410  		input: &vschemapb.Keyspace{
  1411  			Vindexes: map[string]*vschemapb.Vindex{
  1412  				"v1": {
  1413  					Type: "hash",
  1414  				},
  1415  				"v2": {
  1416  					Type: "hash",
  1417  				},
  1418  			},
  1419  		},
  1420  		err: "only one vindex must be specified in the specs",
  1421  	}, {
  1422  		description: "not a lookup",
  1423  		input: &vschemapb.Keyspace{
  1424  			Vindexes: map[string]*vschemapb.Vindex{
  1425  				"v": {
  1426  					Type: "hash",
  1427  				},
  1428  			},
  1429  		},
  1430  		err: "vindex hash is not a lookup type",
  1431  	}, {
  1432  		description: "unqualified table",
  1433  		input: &vschemapb.Keyspace{
  1434  			Vindexes: map[string]*vschemapb.Vindex{
  1435  				"v": {
  1436  					Type: "lookup",
  1437  					Params: map[string]string{
  1438  						"table": "t",
  1439  					},
  1440  				},
  1441  			},
  1442  		},
  1443  		err: "vindex table name must be in the form <keyspace>.<table>",
  1444  	}, {
  1445  		description: "unique lookup should have only one from column",
  1446  		input: &vschemapb.Keyspace{
  1447  			Vindexes: map[string]*vschemapb.Vindex{
  1448  				"v": {
  1449  					Type: "lookup_unique",
  1450  					Params: map[string]string{
  1451  						"table": "targetks.t",
  1452  						"from":  "c1,c2",
  1453  					},
  1454  				},
  1455  			},
  1456  		},
  1457  		err: "unique vindex 'from' should have only one column",
  1458  	}, {
  1459  		description: "non-unique lookup should have more than one column",
  1460  		input: &vschemapb.Keyspace{
  1461  			Vindexes: map[string]*vschemapb.Vindex{
  1462  				"v": {
  1463  					Type: "lookup",
  1464  					Params: map[string]string{
  1465  						"table": "targetks.t",
  1466  						"from":  "c1",
  1467  					},
  1468  				},
  1469  			},
  1470  		},
  1471  		err: "non-unique vindex 'from' should have more than one column",
  1472  	}, {
  1473  		description: "vindex not found",
  1474  		input: &vschemapb.Keyspace{
  1475  			Vindexes: map[string]*vschemapb.Vindex{
  1476  				"v": {
  1477  					Type: "lookup_noexist",
  1478  					Params: map[string]string{
  1479  						"table": "targetks.t",
  1480  						"from":  "c1,c2",
  1481  					},
  1482  				},
  1483  			},
  1484  		},
  1485  		err: `vindexType "lookup_noexist" not found`,
  1486  	}, {
  1487  		description: "only one table",
  1488  		input: &vschemapb.Keyspace{
  1489  			Vindexes: unique,
  1490  		},
  1491  		err: "exactly one table must be specified in the specs",
  1492  	}, {
  1493  		description: "only one colvindex",
  1494  		input: &vschemapb.Keyspace{
  1495  			Vindexes: unique,
  1496  			Tables: map[string]*vschemapb.Table{
  1497  				"t1": {},
  1498  			},
  1499  		},
  1500  		err: "exactly one ColumnVindex must be specified for the table",
  1501  	}, {
  1502  		description: "vindex name must match",
  1503  		input: &vschemapb.Keyspace{
  1504  			Vindexes: unique,
  1505  			Tables: map[string]*vschemapb.Table{
  1506  				"t1": {
  1507  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1508  						Name: "other",
  1509  					}},
  1510  				},
  1511  			},
  1512  		},
  1513  		err: "ColumnVindex name must match vindex name: other vs v",
  1514  	}, {
  1515  		description: "owner must match",
  1516  		input: &vschemapb.Keyspace{
  1517  			Vindexes: map[string]*vschemapb.Vindex{
  1518  				"v": {
  1519  					Type: "lookup_unique",
  1520  					Params: map[string]string{
  1521  						"table": "targetks.t",
  1522  						"from":  "c1",
  1523  					},
  1524  					Owner: "otherTable",
  1525  				},
  1526  			},
  1527  			Tables: map[string]*vschemapb.Table{
  1528  				"t1": {
  1529  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1530  						Name: "v",
  1531  					}},
  1532  				},
  1533  			},
  1534  		},
  1535  		err: "vindex owner must match table name: otherTable vs t1",
  1536  	}, {
  1537  		description: "owner must match",
  1538  		input: &vschemapb.Keyspace{
  1539  			Vindexes: unique,
  1540  			Tables: map[string]*vschemapb.Table{
  1541  				"t1": {
  1542  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1543  						Name: "v",
  1544  					}},
  1545  				},
  1546  			},
  1547  		},
  1548  		err: "at least one column must be specified in ColumnVindexes",
  1549  	}, {
  1550  		description: "columnvindex length mismatch",
  1551  		input: &vschemapb.Keyspace{
  1552  			Vindexes: unique,
  1553  			Tables: map[string]*vschemapb.Table{
  1554  				"t1": {
  1555  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1556  						Name:    "v",
  1557  						Columns: []string{"col1", "col2"},
  1558  					}},
  1559  				},
  1560  			},
  1561  		},
  1562  		err: "length of table columns differes from length of vindex columns",
  1563  	}, {
  1564  		description: "vindex mismatches with what's in vschema",
  1565  		input: &vschemapb.Keyspace{
  1566  			Vindexes: map[string]*vschemapb.Vindex{
  1567  				"other": {
  1568  					Type: "lookup_unique",
  1569  					Params: map[string]string{
  1570  						"table": "targetks.t",
  1571  						"from":  "c1",
  1572  					},
  1573  					Owner: "t1",
  1574  				},
  1575  			},
  1576  			Tables: map[string]*vschemapb.Table{
  1577  				"t1": {
  1578  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1579  						Name:   "other",
  1580  						Column: "col",
  1581  					}},
  1582  				},
  1583  			},
  1584  		},
  1585  		err: "a conflicting vindex named other already exists in the source vschema",
  1586  	}, {
  1587  		description: "source table not in vschema",
  1588  		input: &vschemapb.Keyspace{
  1589  			Vindexes: unique,
  1590  			Tables: map[string]*vschemapb.Table{
  1591  				"other": {
  1592  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1593  						Name:   "v",
  1594  						Column: "col",
  1595  					}},
  1596  				},
  1597  			},
  1598  		},
  1599  		err: "source table other not found in vschema",
  1600  	}, {
  1601  		description: "colvindex already exists in vschema",
  1602  		input: &vschemapb.Keyspace{
  1603  			Vindexes: unique,
  1604  			Tables: map[string]*vschemapb.Table{
  1605  				"t1": {
  1606  					ColumnVindexes: []*vschemapb.ColumnVindex{{
  1607  						Name:   "v",
  1608  						Column: "c1",
  1609  					}},
  1610  				},
  1611  			},
  1612  		},
  1613  		err: "ColumnVindex for table t1 already exists: c1",
  1614  	}}
  1615  	for _, tcase := range testcases {
  1616  		err := wr.CreateLookupVindex(context.Background(), "sourceks", tcase.input, "", "", false)
  1617  		if !strings.Contains(err.Error(), tcase.err) {
  1618  			t.Errorf("CreateLookupVindex(%s) err: %v, must contain %v", tcase.description, err, tcase.err)
  1619  		}
  1620  	}
  1621  }
  1622  
  1623  func TestExternalizeVindex(t *testing.T) {
  1624  	ms := &vtctldatapb.MaterializeSettings{
  1625  		SourceKeyspace: "sourceks",
  1626  		TargetKeyspace: "targetks",
  1627  	}
  1628  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  1629  	defer env.close()
  1630  
  1631  	sourceVSchema := &vschemapb.Keyspace{
  1632  		Sharded: true,
  1633  		Vindexes: map[string]*vschemapb.Vindex{
  1634  			"hash": {
  1635  				Type: "hash",
  1636  			},
  1637  			"owned": {
  1638  				Type: "lookup_unique",
  1639  				Params: map[string]string{
  1640  					"table":      "targetks.lkp",
  1641  					"from":       "c1",
  1642  					"to":         "c2",
  1643  					"write_only": "true",
  1644  				},
  1645  				Owner: "t1",
  1646  			},
  1647  			"unowned": {
  1648  				Type: "lookup_unique",
  1649  				Params: map[string]string{
  1650  					"table":      "targetks.lkp",
  1651  					"from":       "c1",
  1652  					"to":         "c2",
  1653  					"write_only": "true",
  1654  				},
  1655  			},
  1656  			"bad": {
  1657  				Type: "lookup_unique",
  1658  				Params: map[string]string{
  1659  					"table": "unqualified",
  1660  					"from":  "c1",
  1661  					"to":    "c2",
  1662  				},
  1663  			},
  1664  		},
  1665  		Tables: map[string]*vschemapb.Table{
  1666  			"t1": {
  1667  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1668  					Name:   "hash",
  1669  					Column: "col1",
  1670  				}, {
  1671  					Name:   "owned",
  1672  					Column: "col2",
  1673  				}},
  1674  			},
  1675  		},
  1676  	}
  1677  	fields := sqltypes.MakeTestFields(
  1678  		"id|state|message|source",
  1679  		"int64|varbinary|varbinary|blob",
  1680  	)
  1681  	sourceStopAfterCopy := `keyspace:"sourceKs",shard:"0",filter:{rules:{match:"owned" filter:"select * from t1 where in_keyrange(col1, 'sourceKs.hash', '-80')"}} stop_after_copy:true`
  1682  	sourceKeepRunningAfterCopy := `keyspace:"sourceKs",shard:"0",filter:{rules:{match:"owned" filter:"select * from t1 where in_keyrange(col1, 'sourceKs.hash', '-80')"}}`
  1683  	running := sqltypes.MakeTestResult(fields, "1|Running|msg|"+sourceKeepRunningAfterCopy)
  1684  	stopped := sqltypes.MakeTestResult(fields, "1|Stopped|Stopped after copy|"+sourceStopAfterCopy)
  1685  	testcases := []struct {
  1686  		input        string
  1687  		vrResponse   *sqltypes.Result
  1688  		expectDelete bool
  1689  		err          string
  1690  	}{{
  1691  		input:        "sourceks.owned",
  1692  		vrResponse:   stopped,
  1693  		expectDelete: true,
  1694  	}, {
  1695  		input:      "sourceks.unowned",
  1696  		vrResponse: running,
  1697  	}, {
  1698  		input: "unqualified",
  1699  		err:   "vindex name should be of the form keyspace.vindex: unqualified",
  1700  	}, {
  1701  		input: "sourceks.absent",
  1702  		err:   "vindex sourceks.absent not found in vschema",
  1703  	}, {
  1704  		input: "sourceks.bad",
  1705  		err:   "vindex table name must be in the form <keyspace>.<table>. Got: unqualified",
  1706  	}, {
  1707  		input:        "sourceks.owned",
  1708  		vrResponse:   running,
  1709  		expectDelete: true,
  1710  	}, {
  1711  		input:      "sourceks.unowned",
  1712  		vrResponse: stopped,
  1713  		err:        "is not in Running state",
  1714  	}}
  1715  	for _, tcase := range testcases {
  1716  		// Resave the source schema for every iteration.
  1717  		if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, sourceVSchema); err != nil {
  1718  			t.Fatal(err)
  1719  		}
  1720  		if tcase.vrResponse != nil {
  1721  			validationQuery := "select id, state, message, source from _vt.vreplication where workflow='lkp_vdx' and db_name='vt_targetks'"
  1722  			env.tmc.expectVRQuery(200, validationQuery, tcase.vrResponse)
  1723  			env.tmc.expectVRQuery(210, validationQuery, tcase.vrResponse)
  1724  		}
  1725  
  1726  		if tcase.expectDelete {
  1727  			deleteQuery := "delete from _vt.vreplication where db_name='vt_targetks' and workflow='lkp_vdx'"
  1728  			env.tmc.expectVRQuery(200, deleteQuery, &sqltypes.Result{})
  1729  			env.tmc.expectVRQuery(210, deleteQuery, &sqltypes.Result{})
  1730  		}
  1731  
  1732  		err := env.wr.ExternalizeVindex(context.Background(), tcase.input)
  1733  		if tcase.err != "" {
  1734  			if err == nil || !strings.Contains(err.Error(), tcase.err) {
  1735  				t.Errorf("ExternalizeVindex(%s) err: %v, must contain %v", tcase.input, err, tcase.err)
  1736  			}
  1737  			continue
  1738  		}
  1739  		require.NoError(t, err)
  1740  
  1741  		outvschema, err := env.topoServ.GetVSchema(context.Background(), ms.SourceKeyspace)
  1742  		require.NoError(t, err)
  1743  		vindexName := strings.Split(tcase.input, ".")[1]
  1744  		require.NotContains(t, outvschema.Vindexes[vindexName].Params, "write_only", tcase.input)
  1745  	}
  1746  }
  1747  
  1748  func TestMaterializerOneToOne(t *testing.T) {
  1749  	ms := &vtctldatapb.MaterializeSettings{
  1750  		Workflow:       "workflow",
  1751  		SourceKeyspace: "sourceks",
  1752  		TargetKeyspace: "targetks",
  1753  		TableSettings: []*vtctldatapb.TableMaterializeSettings{
  1754  			{
  1755  				TargetTable:      "t1",
  1756  				SourceExpression: "select * from t1",
  1757  				CreateDdl:        "t1ddl",
  1758  			},
  1759  			{
  1760  				TargetTable:      "t2",
  1761  				SourceExpression: "select * from t3",
  1762  				CreateDdl:        "t2ddl",
  1763  			},
  1764  			{
  1765  				TargetTable:      "t4",
  1766  				SourceExpression: "", // empty
  1767  				CreateDdl:        "t4ddl",
  1768  			},
  1769  		},
  1770  		Cell:        "zone1",
  1771  		TabletTypes: "primary,rdonly",
  1772  	}
  1773  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  1774  	defer env.close()
  1775  
  1776  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  1777  	env.tmc.expectVRQuery(
  1778  		200,
  1779  		insertPrefix+
  1780  			`\(`+
  1781  			`'workflow', `+
  1782  			(`'keyspace:\\"sourceks\\" shard:\\"0\\" `+
  1783  				`filter:{`+
  1784  				`rules:{match:\\"t1\\" filter:\\"select.*t1\\"} `+
  1785  				`rules:{match:\\"t2\\" filter:\\"select.*t3\\"} `+
  1786  				`rules:{match:\\"t4\\"}`+
  1787  				`}', `)+
  1788  			`'', [0-9]*, [0-9]*, 'zone1', 'primary,rdonly', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false`+
  1789  			`\)`+eol,
  1790  		&sqltypes.Result{},
  1791  	)
  1792  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  1793  
  1794  	err := env.wr.Materialize(context.Background(), ms)
  1795  	require.NoError(t, err)
  1796  	env.tmc.verifyQueries(t)
  1797  }
  1798  
  1799  func TestMaterializerManyToOne(t *testing.T) {
  1800  	ms := &vtctldatapb.MaterializeSettings{
  1801  		Workflow:       "workflow",
  1802  		SourceKeyspace: "sourceks",
  1803  		TargetKeyspace: "targetks",
  1804  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  1805  			TargetTable:      "t1",
  1806  			SourceExpression: "select * from t1",
  1807  			CreateDdl:        "t1ddl",
  1808  		}, {
  1809  			TargetTable:      "t2",
  1810  			SourceExpression: "select * from t3",
  1811  			CreateDdl:        "t2ddl",
  1812  		}},
  1813  	}
  1814  	env := newTestMaterializerEnv(t, ms, []string{"-80", "80-"}, []string{"0"})
  1815  	defer env.close()
  1816  
  1817  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  1818  	env.tmc.expectVRQuery(
  1819  		200,
  1820  		insertPrefix+
  1821  			`\('workflow', 'keyspace:\\"sourceks\\" shard:\\"-80\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+
  1822  			`, `+
  1823  			`\('workflow', 'keyspace:\\"sourceks\\" shard:\\"80-\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+
  1824  			eol,
  1825  		&sqltypes.Result{},
  1826  	)
  1827  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  1828  
  1829  	err := env.wr.Materialize(context.Background(), ms)
  1830  	require.NoError(t, err)
  1831  	env.tmc.verifyQueries(t)
  1832  }
  1833  
  1834  func TestMaterializerOneToMany(t *testing.T) {
  1835  	ms := &vtctldatapb.MaterializeSettings{
  1836  		Workflow:       "workflow",
  1837  		SourceKeyspace: "sourceks",
  1838  		TargetKeyspace: "targetks",
  1839  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  1840  			TargetTable:      "t1",
  1841  			SourceExpression: "select * from t1",
  1842  			CreateDdl:        "t1ddl",
  1843  		}},
  1844  	}
  1845  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  1846  	defer env.close()
  1847  
  1848  	vs := &vschemapb.Keyspace{
  1849  		Sharded: true,
  1850  		Vindexes: map[string]*vschemapb.Vindex{
  1851  			"hash": {
  1852  				Type: "hash",
  1853  			},
  1854  		},
  1855  		Tables: map[string]*vschemapb.Table{
  1856  			"t1": {
  1857  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1858  					Column: "c1",
  1859  					Name:   "hash",
  1860  				}},
  1861  			},
  1862  		},
  1863  	}
  1864  
  1865  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  1866  		t.Fatal(err)
  1867  	}
  1868  
  1869  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  1870  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  1871  	env.tmc.expectVRQuery(
  1872  		200,
  1873  		insertPrefix+
  1874  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*-80.*`,
  1875  		&sqltypes.Result{},
  1876  	)
  1877  	env.tmc.expectVRQuery(
  1878  		210,
  1879  		insertPrefix+
  1880  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*80-.*`,
  1881  		&sqltypes.Result{},
  1882  	)
  1883  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  1884  	env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{})
  1885  
  1886  	err := env.wr.Materialize(context.Background(), ms)
  1887  	require.NoError(t, err)
  1888  	env.tmc.verifyQueries(t)
  1889  }
  1890  
  1891  func TestMaterializerManyToMany(t *testing.T) {
  1892  	ms := &vtctldatapb.MaterializeSettings{
  1893  		Workflow:       "workflow",
  1894  		SourceKeyspace: "sourceks",
  1895  		TargetKeyspace: "targetks",
  1896  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  1897  			TargetTable:      "t1",
  1898  			SourceExpression: "select * from t1",
  1899  			CreateDdl:        "t1ddl",
  1900  		}},
  1901  	}
  1902  	env := newTestMaterializerEnv(t, ms, []string{"-40", "40-"}, []string{"-80", "80-"})
  1903  	defer env.close()
  1904  
  1905  	vs := &vschemapb.Keyspace{
  1906  		Sharded: true,
  1907  		Vindexes: map[string]*vschemapb.Vindex{
  1908  			"hash": {
  1909  				Type: "hash",
  1910  			},
  1911  		},
  1912  		Tables: map[string]*vschemapb.Table{
  1913  			"t1": {
  1914  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1915  					Column: "c1",
  1916  					Name:   "hash",
  1917  				}},
  1918  			},
  1919  		},
  1920  	}
  1921  
  1922  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  1923  		t.Fatal(err)
  1924  	}
  1925  
  1926  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  1927  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  1928  	env.tmc.expectVRQuery(
  1929  		200,
  1930  		insertPrefix+
  1931  			`.*shard:\\"-40\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*-80.*`+
  1932  			`.*shard:\\"40-\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*-80.*`,
  1933  		&sqltypes.Result{},
  1934  	)
  1935  	env.tmc.expectVRQuery(
  1936  		210,
  1937  		insertPrefix+
  1938  			`.*shard:\\"-40\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*80-.*`+
  1939  			`.*shard:\\"40-\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*80-.*`,
  1940  		&sqltypes.Result{},
  1941  	)
  1942  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  1943  	env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{})
  1944  	err := env.wr.Materialize(context.Background(), ms)
  1945  	require.NoError(t, err)
  1946  	env.tmc.verifyQueries(t)
  1947  }
  1948  
  1949  func TestMaterializerMulticolumnVindex(t *testing.T) {
  1950  	ms := &vtctldatapb.MaterializeSettings{
  1951  		Workflow:       "workflow",
  1952  		SourceKeyspace: "sourceks",
  1953  		TargetKeyspace: "targetks",
  1954  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  1955  			TargetTable:      "t1",
  1956  			SourceExpression: "select * from t1",
  1957  			CreateDdl:        "t1ddl",
  1958  		}},
  1959  	}
  1960  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  1961  	defer env.close()
  1962  
  1963  	vs := &vschemapb.Keyspace{
  1964  		Sharded: true,
  1965  		Vindexes: map[string]*vschemapb.Vindex{
  1966  			"region": {
  1967  				Type: "region_experimental",
  1968  				Params: map[string]string{
  1969  					"region_bytes": "1",
  1970  				},
  1971  			},
  1972  		},
  1973  		Tables: map[string]*vschemapb.Table{
  1974  			"t1": {
  1975  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  1976  					Columns: []string{"c1", "c2"},
  1977  					Name:    "region",
  1978  				}},
  1979  			},
  1980  		},
  1981  	}
  1982  
  1983  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  1984  		t.Fatal(err)
  1985  	}
  1986  
  1987  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  1988  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  1989  	env.tmc.expectVRQuery(
  1990  		200,
  1991  		insertPrefix+
  1992  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*-80.*`,
  1993  		&sqltypes.Result{},
  1994  	)
  1995  	env.tmc.expectVRQuery(
  1996  		210,
  1997  		insertPrefix+
  1998  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*80-.*`,
  1999  		&sqltypes.Result{},
  2000  	)
  2001  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  2002  	env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{})
  2003  
  2004  	err := env.wr.Materialize(context.Background(), ms)
  2005  	require.NoError(t, err)
  2006  	env.tmc.verifyQueries(t)
  2007  }
  2008  
  2009  func TestMaterializerDeploySchema(t *testing.T) {
  2010  	ms := &vtctldatapb.MaterializeSettings{
  2011  		Workflow:       "workflow",
  2012  		SourceKeyspace: "sourceks",
  2013  		TargetKeyspace: "targetks",
  2014  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2015  			TargetTable:      "t1",
  2016  			SourceExpression: "select * from t1",
  2017  			CreateDdl:        "t1ddl",
  2018  		}, {
  2019  			TargetTable:      "t2",
  2020  			SourceExpression: "select * from t3",
  2021  			CreateDdl:        "t2ddl",
  2022  		}},
  2023  	}
  2024  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2025  	defer env.close()
  2026  
  2027  	delete(env.tmc.schema, "targetks.t2")
  2028  
  2029  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2030  	env.tmc.expectVRQuery(200, `t2ddl`, &sqltypes.Result{})
  2031  	env.tmc.expectVRQuery(
  2032  		200,
  2033  		insertPrefix+
  2034  			`\('workflow', 'keyspace:\\"sourceks\\" shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+
  2035  			eol,
  2036  		&sqltypes.Result{},
  2037  	)
  2038  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  2039  
  2040  	err := env.wr.Materialize(context.Background(), ms)
  2041  	require.NoError(t, err)
  2042  	env.tmc.verifyQueries(t)
  2043  	require.Equal(t, env.tmc.getSchemaRequestCount(100), 1)
  2044  	require.Equal(t, env.tmc.getSchemaRequestCount(200), 1)
  2045  }
  2046  
  2047  func TestMaterializerCopySchema(t *testing.T) {
  2048  	ms := &vtctldatapb.MaterializeSettings{
  2049  		Workflow:       "workflow",
  2050  		SourceKeyspace: "sourceks",
  2051  		TargetKeyspace: "targetks",
  2052  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2053  			TargetTable:      "t1",
  2054  			SourceExpression: "select * from t1",
  2055  			CreateDdl:        "copy",
  2056  		}, {
  2057  			TargetTable:      "t2",
  2058  			SourceExpression: "select * from t3",
  2059  			CreateDdl:        "t2ddl",
  2060  		}},
  2061  	}
  2062  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2063  	defer env.close()
  2064  
  2065  	delete(env.tmc.schema, "targetks.t1")
  2066  
  2067  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2068  	env.tmc.expectVRQuery(200, `t1_schema`, &sqltypes.Result{})
  2069  	env.tmc.expectVRQuery(
  2070  		200,
  2071  		insertPrefix+
  2072  			`\('workflow', 'keyspace:\\"sourceks\\" shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+
  2073  			eol,
  2074  		&sqltypes.Result{},
  2075  	)
  2076  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  2077  
  2078  	err := env.wr.Materialize(context.Background(), ms)
  2079  	require.NoError(t, err)
  2080  	env.tmc.verifyQueries(t)
  2081  	require.Equal(t, env.tmc.getSchemaRequestCount(100), 1)
  2082  	require.Equal(t, env.tmc.getSchemaRequestCount(200), 1)
  2083  
  2084  }
  2085  
  2086  func TestMaterializerExplicitColumns(t *testing.T) {
  2087  	ms := &vtctldatapb.MaterializeSettings{
  2088  		Workflow:       "workflow",
  2089  		SourceKeyspace: "sourceks",
  2090  		TargetKeyspace: "targetks",
  2091  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2092  			TargetTable:      "t1",
  2093  			SourceExpression: "select c1, c1+c2, c2 from t1",
  2094  			CreateDdl:        "t1ddl",
  2095  		}},
  2096  	}
  2097  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  2098  	defer env.close()
  2099  
  2100  	vs := &vschemapb.Keyspace{
  2101  		Sharded: true,
  2102  		Vindexes: map[string]*vschemapb.Vindex{
  2103  			"region": {
  2104  				Type: "region_experimental",
  2105  				Params: map[string]string{
  2106  					"region_bytes": "1",
  2107  				},
  2108  			},
  2109  		},
  2110  		Tables: map[string]*vschemapb.Table{
  2111  			"t1": {
  2112  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  2113  					Columns: []string{"c1", "c2"},
  2114  					Name:    "region",
  2115  				}},
  2116  			},
  2117  		},
  2118  	}
  2119  
  2120  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  2121  		t.Fatal(err)
  2122  	}
  2123  
  2124  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2125  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  2126  	env.tmc.expectVRQuery(
  2127  		200,
  2128  		insertPrefix+
  2129  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*-80.*`,
  2130  		&sqltypes.Result{},
  2131  	)
  2132  	env.tmc.expectVRQuery(
  2133  		210,
  2134  		insertPrefix+
  2135  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*80-.*`,
  2136  		&sqltypes.Result{},
  2137  	)
  2138  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  2139  	env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{})
  2140  
  2141  	err := env.wr.Materialize(context.Background(), ms)
  2142  	require.NoError(t, err)
  2143  	env.tmc.verifyQueries(t)
  2144  }
  2145  
  2146  func TestMaterializerRenamedColumns(t *testing.T) {
  2147  	ms := &vtctldatapb.MaterializeSettings{
  2148  		Workflow:       "workflow",
  2149  		SourceKeyspace: "sourceks",
  2150  		TargetKeyspace: "targetks",
  2151  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2152  			TargetTable:      "t1",
  2153  			SourceExpression: "select c3 as c1, c1+c2, c4 as c2 from t1",
  2154  			CreateDdl:        "t1ddl",
  2155  		}},
  2156  	}
  2157  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  2158  	defer env.close()
  2159  
  2160  	vs := &vschemapb.Keyspace{
  2161  		Sharded: true,
  2162  		Vindexes: map[string]*vschemapb.Vindex{
  2163  			"region": {
  2164  				Type: "region_experimental",
  2165  				Params: map[string]string{
  2166  					"region_bytes": "1",
  2167  				},
  2168  			},
  2169  		},
  2170  		Tables: map[string]*vschemapb.Table{
  2171  			"t1": {
  2172  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  2173  					Columns: []string{"c1", "c2"},
  2174  					Name:    "region",
  2175  				}},
  2176  			},
  2177  		},
  2178  	}
  2179  
  2180  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  2181  		t.Fatal(err)
  2182  	}
  2183  
  2184  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2185  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  2186  	env.tmc.expectVRQuery(
  2187  		200,
  2188  		insertPrefix+
  2189  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c3, c4.*targetks\.region.*-80.*`,
  2190  		&sqltypes.Result{},
  2191  	)
  2192  	env.tmc.expectVRQuery(
  2193  		210,
  2194  		insertPrefix+
  2195  			`.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c3, c4.*targetks\.region.*80-.*`,
  2196  		&sqltypes.Result{},
  2197  	)
  2198  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  2199  	env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{})
  2200  
  2201  	err := env.wr.Materialize(context.Background(), ms)
  2202  	require.NoError(t, err)
  2203  	env.tmc.verifyQueries(t)
  2204  }
  2205  
  2206  func TestMaterializerStopAfterCopy(t *testing.T) {
  2207  	ms := &vtctldatapb.MaterializeSettings{
  2208  		Workflow:       "workflow",
  2209  		SourceKeyspace: "sourceks",
  2210  		TargetKeyspace: "targetks",
  2211  		StopAfterCopy:  true,
  2212  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2213  			TargetTable:      "t1",
  2214  			SourceExpression: "select * from t1",
  2215  			CreateDdl:        "t1ddl",
  2216  		}, {
  2217  			TargetTable:      "t2",
  2218  			SourceExpression: "select * from t3",
  2219  			CreateDdl:        "t2ddl",
  2220  		}},
  2221  	}
  2222  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2223  	defer env.close()
  2224  
  2225  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2226  	env.tmc.expectVRQuery(200, insertPrefix+`.*stop_after_copy:true`, &sqltypes.Result{})
  2227  	env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  2228  
  2229  	err := env.wr.Materialize(context.Background(), ms)
  2230  	require.NoError(t, err)
  2231  	env.tmc.verifyQueries(t)
  2232  }
  2233  
  2234  func TestMaterializerNoTargetVSchema(t *testing.T) {
  2235  	ms := &vtctldatapb.MaterializeSettings{
  2236  		Workflow:       "workflow",
  2237  		SourceKeyspace: "sourceks",
  2238  		TargetKeyspace: "targetks",
  2239  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2240  			TargetTable:      "t1",
  2241  			SourceExpression: "select * from t1",
  2242  			CreateDdl:        "t1ddl",
  2243  		}},
  2244  	}
  2245  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  2246  	defer env.close()
  2247  
  2248  	vs := &vschemapb.Keyspace{
  2249  		Sharded: true,
  2250  	}
  2251  
  2252  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  2253  		t.Fatal(err)
  2254  	}
  2255  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2256  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  2257  	err := env.wr.Materialize(context.Background(), ms)
  2258  	require.EqualError(t, err, "table t1 not found in vschema for keyspace targetks")
  2259  }
  2260  
  2261  func TestMaterializerNoDDL(t *testing.T) {
  2262  	ms := &vtctldatapb.MaterializeSettings{
  2263  		Workflow:       "workflow",
  2264  		SourceKeyspace: "sourceks",
  2265  		TargetKeyspace: "targetks",
  2266  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2267  			TargetTable:      "t1",
  2268  			SourceExpression: "select * from t1",
  2269  			CreateDdl:        "",
  2270  		}},
  2271  	}
  2272  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2273  	defer env.close()
  2274  
  2275  	delete(env.tmc.schema, "targetks.t1")
  2276  
  2277  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2278  	err := env.wr.Materialize(context.Background(), ms)
  2279  	require.EqualError(t, err, "target table t1 does not exist and there is no create ddl defined")
  2280  	require.Equal(t, env.tmc.getSchemaRequestCount(100), 0)
  2281  	require.Equal(t, env.tmc.getSchemaRequestCount(200), 1)
  2282  
  2283  }
  2284  
  2285  func TestMaterializerNoSourcePrimary(t *testing.T) {
  2286  	ms := &vtctldatapb.MaterializeSettings{
  2287  		Workflow:       "workflow",
  2288  		SourceKeyspace: "sourceks",
  2289  		TargetKeyspace: "targetks",
  2290  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2291  			TargetTable:      "t1",
  2292  			SourceExpression: "select * from t1",
  2293  			CreateDdl:        "copy",
  2294  		}},
  2295  	}
  2296  	sources := []string{"0"}
  2297  	targets := []string{"0"}
  2298  
  2299  	// Copied from newTestMaterializerEnv
  2300  	env := &testMaterializerEnv{
  2301  		ms:       ms,
  2302  		sources:  sources,
  2303  		targets:  targets,
  2304  		tablets:  make(map[int]*topodatapb.Tablet),
  2305  		topoServ: memorytopo.NewServer("cell"),
  2306  		cell:     "cell",
  2307  		tmc:      newTestMaterializerTMClient(),
  2308  	}
  2309  	env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc)
  2310  	defer env.close()
  2311  
  2312  	tabletID := 100
  2313  	for _, shard := range sources {
  2314  		_ = env.addTablet(tabletID, env.ms.SourceKeyspace, shard, topodatapb.TabletType_REPLICA)
  2315  		tabletID += 10
  2316  	}
  2317  	tabletID = 200
  2318  	for _, shard := range targets {
  2319  		_ = env.addTablet(tabletID, env.ms.TargetKeyspace, shard, topodatapb.TabletType_PRIMARY)
  2320  		tabletID += 10
  2321  	}
  2322  
  2323  	// Skip the schema creation part.
  2324  
  2325  	env.expectValidation()
  2326  
  2327  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2328  	err := env.wr.Materialize(context.Background(), ms)
  2329  	require.EqualError(t, err, "source shard must have a primary for copying schema: 0")
  2330  }
  2331  
  2332  func TestMaterializerTableMismatchNonCopy(t *testing.T) {
  2333  	ms := &vtctldatapb.MaterializeSettings{
  2334  		Workflow:       "workflow",
  2335  		SourceKeyspace: "sourceks",
  2336  		TargetKeyspace: "targetks",
  2337  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2338  			TargetTable:      "t1",
  2339  			SourceExpression: "select * from t2",
  2340  			CreateDdl:        "",
  2341  		}},
  2342  	}
  2343  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2344  	defer env.close()
  2345  
  2346  	delete(env.tmc.schema, "targetks.t1")
  2347  
  2348  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2349  	err := env.wr.Materialize(context.Background(), ms)
  2350  	require.EqualError(t, err, "target table t1 does not exist and there is no create ddl defined")
  2351  }
  2352  
  2353  func TestMaterializerTableMismatchCopy(t *testing.T) {
  2354  	ms := &vtctldatapb.MaterializeSettings{
  2355  		Workflow:       "workflow",
  2356  		SourceKeyspace: "sourceks",
  2357  		TargetKeyspace: "targetks",
  2358  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2359  			TargetTable:      "t1",
  2360  			SourceExpression: "select * from t2",
  2361  			CreateDdl:        "copy",
  2362  		}},
  2363  	}
  2364  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2365  	defer env.close()
  2366  
  2367  	delete(env.tmc.schema, "targetks.t1")
  2368  
  2369  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2370  	err := env.wr.Materialize(context.Background(), ms)
  2371  	require.EqualError(t, err, "source and target table names must match for copying schema: t2 vs t1")
  2372  }
  2373  
  2374  func TestMaterializerNoSourceTable(t *testing.T) {
  2375  	ms := &vtctldatapb.MaterializeSettings{
  2376  		Workflow:       "workflow",
  2377  		SourceKeyspace: "sourceks",
  2378  		TargetKeyspace: "targetks",
  2379  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2380  			TargetTable:      "t1",
  2381  			SourceExpression: "select * from t1",
  2382  			CreateDdl:        "copy",
  2383  		}},
  2384  	}
  2385  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2386  	defer env.close()
  2387  
  2388  	delete(env.tmc.schema, "targetks.t1")
  2389  	delete(env.tmc.schema, "sourceks.t1")
  2390  
  2391  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2392  	err := env.wr.Materialize(context.Background(), ms)
  2393  	require.EqualError(t, err, "source table t1 does not exist")
  2394  }
  2395  
  2396  func TestMaterializerSyntaxError(t *testing.T) {
  2397  	ms := &vtctldatapb.MaterializeSettings{
  2398  		Workflow:       "workflow",
  2399  		SourceKeyspace: "sourceks",
  2400  		TargetKeyspace: "targetks",
  2401  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2402  			TargetTable:      "t1",
  2403  			SourceExpression: "bad query",
  2404  			CreateDdl:        "t1ddl",
  2405  		}},
  2406  	}
  2407  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2408  	defer env.close()
  2409  
  2410  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2411  	err := env.wr.Materialize(context.Background(), ms)
  2412  	require.EqualError(t, err, "syntax error at position 4 near 'bad'")
  2413  }
  2414  
  2415  func TestMaterializerNotASelect(t *testing.T) {
  2416  	ms := &vtctldatapb.MaterializeSettings{
  2417  		Workflow:       "workflow",
  2418  		SourceKeyspace: "sourceks",
  2419  		TargetKeyspace: "targetks",
  2420  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2421  			TargetTable:      "t1",
  2422  			SourceExpression: "update t1 set val=1",
  2423  			CreateDdl:        "t1ddl",
  2424  		}},
  2425  	}
  2426  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2427  	defer env.close()
  2428  
  2429  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2430  	err := env.wr.Materialize(context.Background(), ms)
  2431  	require.EqualError(t, err, "unrecognized statement: update t1 set val=1")
  2432  }
  2433  
  2434  func TestMaterializerNoGoodVindex(t *testing.T) {
  2435  	ms := &vtctldatapb.MaterializeSettings{
  2436  		Workflow:       "workflow",
  2437  		SourceKeyspace: "sourceks",
  2438  		TargetKeyspace: "targetks",
  2439  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2440  			TargetTable:      "t1",
  2441  			SourceExpression: "select * from t1",
  2442  			CreateDdl:        "t1ddl",
  2443  		}},
  2444  	}
  2445  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  2446  	defer env.close()
  2447  
  2448  	vs := &vschemapb.Keyspace{
  2449  		Sharded: true,
  2450  		Vindexes: map[string]*vschemapb.Vindex{
  2451  			"lookup_unique": {
  2452  				Type: "lookup_unique",
  2453  			},
  2454  		},
  2455  		Tables: map[string]*vschemapb.Table{
  2456  			"t1": {
  2457  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  2458  					Column: "c1",
  2459  					Name:   "lookup_unique",
  2460  				}},
  2461  			},
  2462  		},
  2463  	}
  2464  
  2465  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  2466  		t.Fatal(err)
  2467  	}
  2468  
  2469  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2470  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  2471  	err := env.wr.Materialize(context.Background(), ms)
  2472  	require.EqualError(t, err, "could not find a vindex to compute keyspace id for table t1")
  2473  }
  2474  
  2475  func TestMaterializerComplexVindexExpression(t *testing.T) {
  2476  	ms := &vtctldatapb.MaterializeSettings{
  2477  		Workflow:       "workflow",
  2478  		SourceKeyspace: "sourceks",
  2479  		TargetKeyspace: "targetks",
  2480  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2481  			TargetTable:      "t1",
  2482  			SourceExpression: "select a+b as c1 from t1",
  2483  			CreateDdl:        "t1ddl",
  2484  		}},
  2485  	}
  2486  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  2487  	defer env.close()
  2488  
  2489  	vs := &vschemapb.Keyspace{
  2490  		Sharded: true,
  2491  		Vindexes: map[string]*vschemapb.Vindex{
  2492  			"hash": {
  2493  				Type: "hash",
  2494  			},
  2495  		},
  2496  		Tables: map[string]*vschemapb.Table{
  2497  			"t1": {
  2498  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  2499  					Column: "c1",
  2500  					Name:   "hash",
  2501  				}},
  2502  			},
  2503  		},
  2504  	}
  2505  
  2506  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  2507  		t.Fatal(err)
  2508  	}
  2509  
  2510  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2511  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  2512  	err := env.wr.Materialize(context.Background(), ms)
  2513  	require.EqualError(t, err, "vindex column cannot be a complex expression: a + b as c1")
  2514  }
  2515  
  2516  func TestMaterializerNoVindexInExpression(t *testing.T) {
  2517  	ms := &vtctldatapb.MaterializeSettings{
  2518  		Workflow:       "workflow",
  2519  		SourceKeyspace: "sourceks",
  2520  		TargetKeyspace: "targetks",
  2521  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2522  			TargetTable:      "t1",
  2523  			SourceExpression: "select c2 from t1",
  2524  			CreateDdl:        "t1ddl",
  2525  		}},
  2526  	}
  2527  	env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"})
  2528  	defer env.close()
  2529  
  2530  	vs := &vschemapb.Keyspace{
  2531  		Sharded: true,
  2532  		Vindexes: map[string]*vschemapb.Vindex{
  2533  			"hash": {
  2534  				Type: "hash",
  2535  			},
  2536  		},
  2537  		Tables: map[string]*vschemapb.Table{
  2538  			"t1": {
  2539  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  2540  					Column: "c1",
  2541  					Name:   "hash",
  2542  				}},
  2543  			},
  2544  		},
  2545  	}
  2546  
  2547  	if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  2548  		t.Fatal(err)
  2549  	}
  2550  
  2551  	env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2552  	env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{})
  2553  	err := env.wr.Materialize(context.Background(), ms)
  2554  	require.EqualError(t, err, "could not find vindex column c1")
  2555  }
  2556  
  2557  func TestStripForeignKeys(t *testing.T) {
  2558  	tcs := []struct {
  2559  		desc string
  2560  		ddl  string
  2561  
  2562  		hasErr bool
  2563  		newDDL string
  2564  	}{
  2565  		{
  2566  			desc: "has FK constraints",
  2567  			ddl: "CREATE TABLE `table1` (\n" +
  2568  				"`id` int(11) NOT NULL AUTO_INCREMENT,\n" +
  2569  				"`foreign_id` int(11) CHECK (foreign_id>10),\n" +
  2570  				"PRIMARY KEY (`id`),\n" +
  2571  				"KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" +
  2572  				"CONSTRAINT `fk_table1_ref_foreign_id` FOREIGN KEY (`foreign_id`) REFERENCES `foreign` (`id`)\n" +
  2573  				") ENGINE=InnoDB DEFAULT CHARSET=latin1;",
  2574  
  2575  			newDDL: "create table table1 (\n" +
  2576  				"\tid int(11) not null auto_increment,\n" +
  2577  				"\tforeign_id int(11),\n" +
  2578  				"\tPRIMARY KEY (id),\n" +
  2579  				"\tKEY fk_table1_ref_foreign_id (foreign_id),\n" +
  2580  				"\tcheck (foreign_id > 10)\n" +
  2581  				") ENGINE InnoDB,\n" +
  2582  				"  CHARSET latin1",
  2583  
  2584  			hasErr: false,
  2585  		},
  2586  		{
  2587  			desc: "no FK constraints",
  2588  			ddl: "CREATE TABLE `table1` (\n" +
  2589  				"`id` int(11) NOT NULL AUTO_INCREMENT,\n" +
  2590  				"`foreign_id` int(11) NOT NULL  CHECK (foreign_id>10),\n" +
  2591  				"`user_id` int(11) NOT NULL,\n" +
  2592  				"PRIMARY KEY (`id`),\n" +
  2593  				"KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" +
  2594  				"KEY `fk_table1_ref_user_id` (`user_id`)\n" +
  2595  				") ENGINE=InnoDB DEFAULT CHARSET=latin1;",
  2596  
  2597  			newDDL: "create table table1 (\n" +
  2598  				"\tid int(11) not null auto_increment,\n" +
  2599  				"\tforeign_id int(11) not null,\n" +
  2600  				"\tuser_id int(11) not null,\n" +
  2601  				"\tPRIMARY KEY (id),\n" +
  2602  				"\tKEY fk_table1_ref_foreign_id (foreign_id),\n" +
  2603  				"\tKEY fk_table1_ref_user_id (user_id),\n" +
  2604  				"\tcheck (foreign_id > 10)\n" +
  2605  				") ENGINE InnoDB,\n" +
  2606  				"  CHARSET latin1",
  2607  		},
  2608  	}
  2609  
  2610  	for _, tc := range tcs {
  2611  		newDDL, err := stripTableForeignKeys(tc.ddl)
  2612  		if tc.hasErr != (err != nil) {
  2613  			t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc)
  2614  		}
  2615  
  2616  		if newDDL != tc.newDDL {
  2617  			utils.MustMatch(t, tc.newDDL, newDDL, fmt.Sprintf("newDDL does not match. tc: %+v", tc))
  2618  		}
  2619  	}
  2620  }
  2621  
  2622  func TestStripConstraints(t *testing.T) {
  2623  	tcs := []struct {
  2624  		desc string
  2625  		ddl  string
  2626  
  2627  		hasErr bool
  2628  		newDDL string
  2629  	}{
  2630  		{
  2631  			desc: "constraints",
  2632  			ddl: "CREATE TABLE `table1` (\n" +
  2633  				"`id` int(11) NOT NULL AUTO_INCREMENT,\n" +
  2634  				"`foreign_id` int(11) NOT NULL,\n" +
  2635  				"`user_id` int(11) NOT NULL,\n" +
  2636  				"PRIMARY KEY (`id`),\n" +
  2637  				"KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" +
  2638  				"KEY `fk_table1_ref_user_id` (`user_id`),\n" +
  2639  				"CONSTRAINT `fk_table1_ref_foreign_id` FOREIGN KEY (`foreign_id`) REFERENCES `foreign` (`id`),\n" +
  2640  				"CONSTRAINT `fk_table1_ref_user_id` FOREIGN KEY (`user_id`) REFERENCES `core_user` (`id`)\n" +
  2641  				") ENGINE=InnoDB DEFAULT CHARSET=latin1;",
  2642  
  2643  			newDDL: "create table table1 (\n" +
  2644  				"\tid int(11) not null auto_increment,\n" +
  2645  				"\tforeign_id int(11) not null,\n" +
  2646  				"\tuser_id int(11) not null,\n" +
  2647  				"\tPRIMARY KEY (id),\n" +
  2648  				"\tKEY fk_table1_ref_foreign_id (foreign_id),\n" +
  2649  				"\tKEY fk_table1_ref_user_id (user_id)\n" +
  2650  				") ENGINE InnoDB,\n" +
  2651  				"  CHARSET latin1",
  2652  
  2653  			hasErr: false,
  2654  		},
  2655  		{
  2656  			desc: "no constraints",
  2657  			ddl: "CREATE TABLE `table1` (\n" +
  2658  				"`id` int(11) NOT NULL AUTO_INCREMENT,\n" +
  2659  				"`foreign_id` int(11) NOT NULL,\n" +
  2660  				"`user_id` int(11) NOT NULL,\n" +
  2661  				"PRIMARY KEY (`id`),\n" +
  2662  				"KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" +
  2663  				"KEY `fk_table1_ref_user_id` (`user_id`)\n" +
  2664  				") ENGINE=InnoDB DEFAULT CHARSET=latin1;",
  2665  
  2666  			newDDL: "create table table1 (\n" +
  2667  				"\tid int(11) not null auto_increment,\n" +
  2668  				"\tforeign_id int(11) not null,\n" +
  2669  				"\tuser_id int(11) not null,\n" +
  2670  				"\tPRIMARY KEY (id),\n" +
  2671  				"\tKEY fk_table1_ref_foreign_id (foreign_id),\n" +
  2672  				"\tKEY fk_table1_ref_user_id (user_id)\n" +
  2673  				") ENGINE InnoDB,\n" +
  2674  				"  CHARSET latin1",
  2675  		},
  2676  		{
  2677  			desc: "bad ddl has error",
  2678  			ddl:  "bad ddl",
  2679  
  2680  			hasErr: true,
  2681  		},
  2682  	}
  2683  
  2684  	for _, tc := range tcs {
  2685  		newDDL, err := stripTableConstraints(tc.ddl)
  2686  		if tc.hasErr != (err != nil) {
  2687  			t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc)
  2688  		}
  2689  
  2690  		if newDDL != tc.newDDL {
  2691  			utils.MustMatch(t, tc.newDDL, newDDL, fmt.Sprintf("newDDL does not match. tc: %+v", tc))
  2692  		}
  2693  	}
  2694  }
  2695  
  2696  func TestMaterializerManyToManySomeUnreachable(t *testing.T) {
  2697  	ms := &vtctldatapb.MaterializeSettings{
  2698  		Workflow:       "workflow",
  2699  		SourceKeyspace: "sourceks",
  2700  		TargetKeyspace: "targetks",
  2701  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2702  			TargetTable:      "t1",
  2703  			SourceExpression: "select * from t1",
  2704  			CreateDdl:        "t1ddl",
  2705  		}},
  2706  	}
  2707  
  2708  	vs := &vschemapb.Keyspace{
  2709  		Sharded: true,
  2710  		Vindexes: map[string]*vschemapb.Vindex{
  2711  			"hash": {
  2712  				Type: "hash",
  2713  			},
  2714  		},
  2715  		Tables: map[string]*vschemapb.Table{
  2716  			"t1": {
  2717  				ColumnVindexes: []*vschemapb.ColumnVindex{{
  2718  					Column: "c1",
  2719  					Name:   "hash",
  2720  				}},
  2721  			},
  2722  		},
  2723  	}
  2724  	type testcase struct {
  2725  		targetShards, sourceShards []string
  2726  		insertMap                  map[string][]string
  2727  	}
  2728  	testcases := []testcase{
  2729  		{
  2730  			targetShards: []string{"-40", "40-80", "80-c0", "c0-"},
  2731  			sourceShards: []string{"-80", "80-"},
  2732  			insertMap:    map[string][]string{"-40": {"-80"}, "40-80": {"-80"}, "80-c0": {"80-"}, "c0-": {"80-"}},
  2733  		},
  2734  		{
  2735  			targetShards: []string{"-20", "20-40", "40-a0", "a0-f0", "f0-"},
  2736  			sourceShards: []string{"-40", "40-80", "80-c0", "c0-"},
  2737  			insertMap:    map[string][]string{"-20": {"-40"}, "20-40": {"-40"}, "40-a0": {"40-80", "80-c0"}, "a0-f0": {"80-c0", "c0-"}, "f0-": {"c0-"}},
  2738  		},
  2739  		{
  2740  			targetShards: []string{"-40", "40-80", "80-"},
  2741  			sourceShards: []string{"-80", "80-"},
  2742  			insertMap:    map[string][]string{"-40": {"-80"}, "40-80": {"-80"}, "80-": {"80-"}},
  2743  		},
  2744  		{
  2745  			targetShards: []string{"-80", "80-"},
  2746  			sourceShards: []string{"-40", "40-80", "80-c0", "c0-"},
  2747  			insertMap:    map[string][]string{"-80": {"-40", "40-80"}, "80-": {"80-c0", "c0-"}},
  2748  		},
  2749  		{
  2750  			targetShards: []string{"0"},
  2751  			sourceShards: []string{"-80", "80-"},
  2752  			insertMap:    map[string][]string{"0": {"-80", "80-"}},
  2753  		},
  2754  		{
  2755  			targetShards: []string{"-80", "80-"},
  2756  			sourceShards: []string{"0"},
  2757  			insertMap:    map[string][]string{"-80": {"0"}, "80-": {"0"}},
  2758  		},
  2759  	}
  2760  
  2761  	getStreamInsert := func(sourceShard, targetShard string) string {
  2762  		return fmt.Sprintf(`.*shard:\\"%s\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*%s.*`, sourceShard, targetShard)
  2763  	}
  2764  
  2765  	for _, tcase := range testcases {
  2766  		t.Run("", func(t *testing.T) {
  2767  			env := newTestMaterializerEnv(t, ms, tcase.sourceShards, tcase.targetShards)
  2768  			if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil {
  2769  				t.Fatal(err)
  2770  			}
  2771  			defer env.close()
  2772  			for i, targetShard := range tcase.targetShards {
  2773  				tabletID := 200 + i*10
  2774  				env.tmc.expectVRQuery(tabletID, mzSelectFrozenQuery, &sqltypes.Result{})
  2775  
  2776  				streamsInsert := ""
  2777  				sourceShards := tcase.insertMap[targetShard]
  2778  				for _, sourceShard := range sourceShards {
  2779  					streamsInsert += getStreamInsert(sourceShard, targetShard)
  2780  				}
  2781  				env.tmc.expectVRQuery(
  2782  					tabletID,
  2783  					insertPrefix+streamsInsert,
  2784  					&sqltypes.Result{},
  2785  				)
  2786  				env.tmc.expectVRQuery(tabletID, mzUpdateQuery, &sqltypes.Result{})
  2787  			}
  2788  			err := env.wr.Materialize(context.Background(), ms)
  2789  			require.NoError(t, err)
  2790  			env.tmc.verifyQueries(t)
  2791  		})
  2792  	}
  2793  }
  2794  
  2795  // TestMoveTablesDDLFlag tests that we save the on-ddl flag value in the workflow.
  2796  // Note:
  2797  //   - TestPlayerDDL tests that the vplayer correctly implements the ddl behavior
  2798  //   - We have a manual e2e test for the full behavior: TestVReplicationDDLHandling
  2799  func TestMoveTablesDDLFlag(t *testing.T) {
  2800  	ms := &vtctldatapb.MaterializeSettings{
  2801  		Workflow:       "workflow",
  2802  		SourceKeyspace: "sourceks",
  2803  		TargetKeyspace: "targetks",
  2804  		TableSettings: []*vtctldatapb.TableMaterializeSettings{{
  2805  			TargetTable:      "t1",
  2806  			SourceExpression: "select * from t1",
  2807  		}},
  2808  	}
  2809  	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
  2810  	defer cancel()
  2811  
  2812  	for onDDLAction := range binlogdatapb.OnDDLAction_value {
  2813  		t.Run(fmt.Sprintf("OnDDL Flag:%v", onDDLAction), func(t *testing.T) {
  2814  			env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"})
  2815  			defer env.close()
  2816  
  2817  			env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{})
  2818  			env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{})
  2819  			if onDDLAction == binlogdatapb.OnDDLAction_name[int32(binlogdatapb.OnDDLAction_IGNORE)] {
  2820  				// This is the default and go does not marshal defaults
  2821  				// for prototext fields so we use the default insert stmt.
  2822  				env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{})
  2823  			} else {
  2824  				env.tmc.expectVRQuery(200, fmt.Sprintf(`/insert into _vt.vreplication\(.*on_ddl:%s.*`, onDDLAction),
  2825  					&sqltypes.Result{})
  2826  			}
  2827  			env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{})
  2828  			env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{})
  2829  
  2830  			err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "",
  2831  				"", false, "", false, true, "", false, false, "", onDDLAction, nil)
  2832  			require.NoError(t, err)
  2833  		})
  2834  	}
  2835  }