vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/lookup_test.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package vtgate
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"vitess.io/vitess/go/test/endtoend/utils"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"vitess.io/vitess/go/mysql"
    31  )
    32  
    33  func TestUnownedLookupInsertNull(t *testing.T) {
    34  	conn, closer := start(t)
    35  	defer closer()
    36  
    37  	utils.Exec(t, conn, "insert into t9(id, parent_id) VALUES (1, 1)")
    38  	utils.Exec(t, conn, "insert into t9(id, parent_id) VALUES (2, 2)")
    39  
    40  	utils.Exec(t, conn, "insert into t8(id, parent_id, t9_id) VALUES (1, 1, NULL)")
    41  	utils.Exec(t, conn, "insert into t8(id, parent_id, t9_id) VALUES (2, 1, 1)")
    42  	utils.Exec(t, conn, "insert into t8(id, parent_id, t9_id) VALUES (3, 2, 2)")
    43  }
    44  
    45  func TestLookupUniqueWithAutocommit(t *testing.T) {
    46  	conn, closer := start(t)
    47  	defer closer()
    48  
    49  	// conn2 is to check entries in the lookup table
    50  	conn2, err := mysql.Connect(context.Background(), &vtParams)
    51  	require.Nil(t, err)
    52  	defer conn2.Close()
    53  
    54  	// Test that all vindex writes are autocommitted outside of any ongoing transactions.
    55  	//
    56  	// Also test that autocommited vindex entries are visible inside transactions, as lookups
    57  	// should also use the autocommit connection.
    58  
    59  	utils.Exec(t, conn, "insert into t10(id, sharding_key) VALUES (1, 1)")
    60  
    61  	utils.AssertMatches(t, conn2, "select id from t10_id_to_keyspace_id_idx order by id asc", "[[INT64(1)]]")
    62  	utils.AssertMatches(t, conn, "select id from t10 where id = 1", "[[INT64(1)]]")
    63  
    64  	utils.Exec(t, conn, "begin")
    65  
    66  	utils.Exec(t, conn, "insert into t10(id, sharding_key) VALUES (2, 1)")
    67  
    68  	utils.AssertMatches(t, conn2, "select id from t10_id_to_keyspace_id_idx order by id asc", "[[INT64(1)] [INT64(2)]]")
    69  	utils.AssertMatches(t, conn, "select id from t10 where id = 2", "[[INT64(2)]]")
    70  
    71  	utils.Exec(t, conn, "insert into t10(id, sharding_key) VALUES (3, 1)")
    72  
    73  	utils.AssertMatches(t, conn2, "select id from t10_id_to_keyspace_id_idx order by id asc", "[[INT64(1)] [INT64(2)] [INT64(3)]]")
    74  	utils.AssertMatches(t, conn, "select id from t10 where id = 3", "[[INT64(3)]]")
    75  
    76  	utils.Exec(t, conn, "savepoint sp_foobar")
    77  
    78  	utils.Exec(t, conn, "insert into t10(id, sharding_key) VALUES (4, 1)")
    79  
    80  	utils.AssertMatches(t, conn2, "select id from t10_id_to_keyspace_id_idx order by id asc", "[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]")
    81  	utils.AssertMatches(t, conn, "select id from t10 where id = 4", "[[INT64(4)]]")
    82  }
    83  
    84  func TestUnownedLookupInsertChecksKeyspaceIdsAreMatching(t *testing.T) {
    85  	conn, closer := start(t)
    86  	defer closer()
    87  
    88  	utils.Exec(t, conn, "insert into t9(id, parent_id) VALUES (1, 1)")
    89  
    90  	// This fails because the keyspace id for `parent_id` does not match the one for `t9_id`
    91  	_, err := utils.ExecAllowError(t, conn, "insert into t8(id, parent_id, t9_id) VALUES (4, 2, 1)")
    92  	require.EqualError(t, err, "values [[INT64(1)]] for column [t9_id] does not map to keyspace ids (errno 1105) (sqlstate HY000) during query: insert into t8(id, parent_id, t9_id) VALUES (4, 2, 1)")
    93  
    94  	// This fails because the `t9_id` value can't be mapped to a keyspace id
    95  	_, err = utils.ExecAllowError(t, conn, "insert into t8(id, parent_id, t9_id) VALUES (4, 2, 2)")
    96  	require.EqualError(t, err, "values [[INT64(2)]] for column [t9_id] does not map to keyspace ids (errno 1105) (sqlstate HY000) during query: insert into t8(id, parent_id, t9_id) VALUES (4, 2, 2)")
    97  }
    98  
    99  func TestUnownedLookupSelectNull(t *testing.T) {
   100  	conn, closer := start(t)
   101  	defer closer()
   102  
   103  	utils.Exec(t, conn, "select * from t8 WHERE t9_id IS NULL")
   104  }
   105  
   106  func TestConsistentLookup(t *testing.T) {
   107  	conn, closer := start(t)
   108  	defer closer()
   109  	// conn2 is for queries that target shards.
   110  	conn2, err := mysql.Connect(context.Background(), &vtParams)
   111  	require.Nil(t, err)
   112  	defer conn2.Close()
   113  
   114  	// Simple insert.
   115  	utils.Exec(t, conn, "begin")
   116  	utils.Exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
   117  	// check that the lookup query happens in the right connection
   118  	utils.AssertMatches(t, conn, "select * from t1 where id2 = 4", "[[INT64(1) INT64(4)]]")
   119  	utils.Exec(t, conn, "commit")
   120  	utils.AssertMatches(t, conn, "select * from t1", "[[INT64(1) INT64(4)]]")
   121  	qr := utils.Exec(t, conn, "select * from t1_id2_idx")
   122  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   123  		t.Errorf("select:\n%v want\n%v", got, want)
   124  	}
   125  
   126  	// Inserting again should fail.
   127  	utils.Exec(t, conn, "begin")
   128  	_, err = conn.ExecuteFetch("insert into t1(id1, id2) values(1, 4)", 1000, false)
   129  	utils.Exec(t, conn, "rollback")
   130  	require.Error(t, err)
   131  	mysqlErr := err.(*mysql.SQLError)
   132  	assert.Equal(t, 1062, mysqlErr.Num)
   133  	assert.Equal(t, "23000", mysqlErr.State)
   134  	assert.Contains(t, mysqlErr.Message, "reverted partial DML execution")
   135  
   136  	// Simple delete.
   137  	utils.Exec(t, conn, "begin")
   138  	utils.Exec(t, conn, "delete from t1 where id1=1")
   139  	utils.AssertMatches(t, conn, "select * from t1 where id2 = 4", "[]")
   140  	utils.Exec(t, conn, "commit")
   141  	qr = utils.Exec(t, conn, "select * from t1")
   142  	if got, want := fmt.Sprintf("%v", qr.Rows), "[]"; got != want {
   143  		t.Errorf("select:\n%v want\n%v", got, want)
   144  	}
   145  	qr = utils.Exec(t, conn, "select * from t1_id2_idx")
   146  	if got, want := fmt.Sprintf("%v", qr.Rows), "[]"; got != want {
   147  		t.Errorf("select:\n%v want\n%v", got, want)
   148  	}
   149  
   150  	// Autocommit insert.
   151  	utils.Exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
   152  	qr = utils.Exec(t, conn, "select * from t1")
   153  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
   154  		t.Errorf("select:\n%v want\n%v", got, want)
   155  	}
   156  	qr = utils.Exec(t, conn, "select id2 from t1_id2_idx")
   157  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4)]]"; got != want {
   158  		t.Errorf("select:\n%v want\n%v", got, want)
   159  	}
   160  	// Autocommit delete.
   161  	utils.Exec(t, conn, "delete from t1 where id1=1")
   162  
   163  	// Dangling row pointing to existing keyspace id.
   164  	utils.Exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
   165  	// Delete the main row only.
   166  	utils.Exec(t, conn2, "use `ks:-80`")
   167  	utils.Exec(t, conn2, "delete from t1 where id1=1")
   168  	// Verify the lookup row is still there.
   169  	qr = utils.Exec(t, conn, "select id2 from t1_id2_idx")
   170  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4)]]"; got != want {
   171  		t.Errorf("select:\n%v want\n%v", got, want)
   172  	}
   173  	// Insert should still succeed.
   174  	utils.Exec(t, conn, "begin")
   175  	utils.Exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
   176  	utils.Exec(t, conn, "commit")
   177  	qr = utils.Exec(t, conn, "select * from t1")
   178  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
   179  		t.Errorf("select:\n%v want\n%v", got, want)
   180  	}
   181  	// Lookup row should be unchanged.
   182  	qr = utils.Exec(t, conn, "select * from t1_id2_idx")
   183  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   184  		t.Errorf("select:\n%v want\n%v", got, want)
   185  	}
   186  
   187  	// Dangling row not pointing to existing keyspace id.
   188  	utils.Exec(t, conn2, "use `ks:-80`")
   189  	utils.Exec(t, conn2, "delete from t1 where id1=1")
   190  	// Update the lookup row with bogus keyspace id.
   191  	utils.Exec(t, conn, "update t1_id2_idx set keyspace_id='aaa' where id2=4")
   192  	qr = utils.Exec(t, conn, "select * from t1_id2_idx")
   193  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"aaa\")]]"; got != want {
   194  		t.Errorf("select:\n%v want\n%v", got, want)
   195  	}
   196  	// Insert should still succeed.
   197  	utils.Exec(t, conn, "begin")
   198  	utils.Exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
   199  	utils.Exec(t, conn, "commit")
   200  	qr = utils.Exec(t, conn, "select * from t1")
   201  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
   202  		t.Errorf("select:\n%v want\n%v", got, want)
   203  	}
   204  	// lookup row must be updated.
   205  	qr = utils.Exec(t, conn, "select * from t1_id2_idx")
   206  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   207  		t.Errorf("select:\n%v want\n%v", got, want)
   208  	}
   209  
   210  	// Update, but don't change anything. This should not deadlock.
   211  	utils.Exec(t, conn, "begin")
   212  	utils.Exec(t, conn, "update t1 set id2=4 where id1=1")
   213  	utils.Exec(t, conn, "commit")
   214  	qr = utils.Exec(t, conn, "select * from t1")
   215  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
   216  		t.Errorf("select:\n%v want\n%v", got, want)
   217  	}
   218  	qr = utils.Exec(t, conn, "select * from t1_id2_idx")
   219  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   220  		t.Errorf("select:\n%v want\n%v", got, want)
   221  	}
   222  
   223  	// Update, and change the lookup value. This should change main and lookup rows.
   224  	utils.Exec(t, conn, "begin")
   225  	utils.Exec(t, conn, "update t1 set id2=5 where id1=1")
   226  	utils.Exec(t, conn, "commit")
   227  	qr = utils.Exec(t, conn, "select * from t1")
   228  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(5)]]"; got != want {
   229  		t.Errorf("select:\n%v want\n%v", got, want)
   230  	}
   231  	qr = utils.Exec(t, conn, "select * from t1_id2_idx")
   232  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(5) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   233  		t.Errorf("select:\n%v want\n%v", got, want)
   234  	}
   235  }
   236  
   237  func TestDMLScatter(t *testing.T) {
   238  	conn, closer := start(t)
   239  	defer closer()
   240  
   241  	/* Simple insert. after this dml, the tables will contain the following:
   242  	t3 (id5, id6, id7):
   243  	1 2 3
   244  	2 2 3
   245  	3 4 3
   246  	4 5 4
   247  
   248  	t3_id7_idx (id7, keyspace_id:id6):
   249  	3 2
   250  	3 2
   251  	3 4
   252  	4 5
   253  	*/
   254  	utils.Exec(t, conn, "begin")
   255  	utils.Exec(t, conn, "insert into t3(id5, id6, id7) values(1, 2, 3), (2, 2, 3), (3, 4, 3), (4, 5, 4)")
   256  	utils.Exec(t, conn, "commit")
   257  	qr := utils.Exec(t, conn, "select id5, id6, id7 from t3 order by id5")
   258  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(2) INT64(3)] [INT64(2) INT64(2) INT64(3)] [INT64(3) INT64(4) INT64(3)] [INT64(4) INT64(5) INT64(4)]]"; got != want {
   259  		t.Errorf("select:\n%v want\n%v", got, want)
   260  	}
   261  
   262  	/* Updating a non lookup column. after this dml, the tables will contain the following:
   263  	t3 (id5, id6, id7):
   264  	42 2 3
   265  	2 2 3
   266  	3 4 3
   267  	4 5 4
   268  
   269  	t3_id7_idx (id7, keyspace_id:id6):
   270  	3 2
   271  	3 2
   272  	3 4
   273  	4 5
   274  	*/
   275  	utils.Exec(t, conn, "update `ks[-]`.t3 set id5 = 42 where id5 = 1")
   276  	qr = utils.Exec(t, conn, "select id5, id6, id7 from t3 order by id5")
   277  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(2) INT64(2) INT64(3)] [INT64(3) INT64(4) INT64(3)] [INT64(4) INT64(5) INT64(4)] [INT64(42) INT64(2) INT64(3)]]"; got != want {
   278  		t.Errorf("select:\n%v want\n%v", got, want)
   279  	}
   280  
   281  	/* Updating a lookup column. after this dml, the tables will contain the following:
   282  	t3 (id5, id6, id7):
   283  	42 2 42
   284  	2 2 42
   285  	3 4 3
   286  	4 5 4
   287  
   288  	t3_id7_idx (id7, keyspace_id:id6):
   289  	42 2
   290  	42 2
   291  	3 4
   292  	4 5
   293  	*/
   294  	utils.Exec(t, conn, "begin")
   295  	utils.Exec(t, conn, "update t3 set id7 = 42 where id6 = 2")
   296  	utils.Exec(t, conn, "commit")
   297  	qr = utils.Exec(t, conn, "select id5, id6, id7 from t3 order by id5")
   298  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(2) INT64(2) INT64(42)] [INT64(3) INT64(4) INT64(3)] [INT64(4) INT64(5) INT64(4)] [INT64(42) INT64(2) INT64(42)]]"; got != want {
   299  		t.Errorf("select:\n%v want\n%v", got, want)
   300  	}
   301  
   302  	/* delete one specific keyspace id. after this dml, the tables will contain the following:
   303  	t3 (id5, id6, id7):
   304  	3 4 3
   305  	4 5 4
   306  
   307  	t3_id7_idx (id7, keyspace_id:id6):
   308  	3 4
   309  	4 5
   310  	*/
   311  	utils.Exec(t, conn, "delete from t3 where id6 = 2")
   312  	qr = utils.Exec(t, conn, "select * from t3 where id6 = 2")
   313  	require.Empty(t, qr.Rows)
   314  	qr = utils.Exec(t, conn, "select * from t3_id7_idx where id6 = 2")
   315  	require.Empty(t, qr.Rows)
   316  
   317  	// delete all the rows.
   318  	utils.Exec(t, conn, "delete from `ks[-]`.t3")
   319  	qr = utils.Exec(t, conn, "select * from t3")
   320  	require.Empty(t, qr.Rows)
   321  	qr = utils.Exec(t, conn, "select * from t3_id7_idx")
   322  	require.Empty(t, qr.Rows)
   323  }
   324  
   325  func TestDMLIn(t *testing.T) {
   326  	conn, closer := start(t)
   327  	defer closer()
   328  
   329  	/* Simple insert. after this dml, the tables will contain the following:
   330  	t3 (id5, id6, id7):
   331  	1 2 3
   332  	2 2 3
   333  	3 4 3
   334  	4 5 4
   335  
   336  	t3_id7_idx (id7, keyspace_id:id6):
   337  	3 2
   338  	3 2
   339  	3 4
   340  	4 5
   341  	*/
   342  	utils.Exec(t, conn, "begin")
   343  	utils.Exec(t, conn, "insert into t3(id5, id6, id7) values(1, 2, 3), (2, 2, 3), (3, 4, 3), (4, 5, 4)")
   344  	utils.Exec(t, conn, "commit")
   345  	qr := utils.Exec(t, conn, "select id5, id6, id7 from t3 order by id5, id6")
   346  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(2) INT64(3)] [INT64(2) INT64(2) INT64(3)] [INT64(3) INT64(4) INT64(3)] [INT64(4) INT64(5) INT64(4)]]"; got != want {
   347  		t.Errorf("select:\n%v want\n%v", got, want)
   348  	}
   349  
   350  	/* Updating a non lookup column. after this dml, the tables will contain the following:
   351  	t3 (id5, id6, id7):
   352  	1 2 3
   353  	2 2 3
   354  	42 4 3
   355  	42 5 4
   356  
   357  	t3_id7_idx (id7, keyspace_id:id6):
   358  	3 2
   359  	3 2
   360  	3 4
   361  	4 5
   362  	*/
   363  	utils.Exec(t, conn, "update t3 set id5 = 42 where id6 in (4, 5)")
   364  	qr = utils.Exec(t, conn, "select id5, id6, id7 from t3 order by id5, id6")
   365  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(2) INT64(3)] [INT64(2) INT64(2) INT64(3)] [INT64(42) INT64(4) INT64(3)] [INT64(42) INT64(5) INT64(4)]]"; got != want {
   366  		t.Errorf("select:\n%v want\n%v", got, want)
   367  	}
   368  
   369  	/* Updating a non lookup column. after this dml, the tables will contain the following:
   370  	t3 (id5, id6, id7):
   371  	1 2 42
   372  	2 2 42
   373  	42 4 3
   374  	42 5 4
   375  
   376  	t3_id7_idx (id7, keyspace_id:id6):
   377  	42 2
   378  	42 2
   379  	3 4
   380  	42 5
   381  	*/
   382  	utils.Exec(t, conn, "begin")
   383  	utils.Exec(t, conn, "update t3 set id7 = 42 where id6 in (2, 5)")
   384  	utils.Exec(t, conn, "commit")
   385  	qr = utils.Exec(t, conn, "select id5, id6, id7 from t3 order by id5, id6")
   386  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(2) INT64(42)] [INT64(2) INT64(2) INT64(42)] [INT64(42) INT64(4) INT64(3)] [INT64(42) INT64(5) INT64(42)]]"; got != want {
   387  		t.Errorf("select:\n%v want\n%v", got, want)
   388  	}
   389  
   390  	/* Updating a non lookup column. after this dml, the tables will contain the following:
   391  	t3 (id5, id6, id7):
   392  	42 4 3
   393  	42 5 4
   394  
   395  	t3_id7_idx (id7, keyspace_id:id6):
   396  	3 4
   397  	42 5
   398  	*/
   399  	utils.Exec(t, conn, "delete from t3 where id6 in (2)")
   400  	qr = utils.Exec(t, conn, "select * from t3 where id6 = 2")
   401  	require.Empty(t, qr.Rows)
   402  	qr = utils.Exec(t, conn, "select * from t3_id7_idx where id6 = 2")
   403  	require.Empty(t, qr.Rows)
   404  
   405  	// delete all the rows.
   406  	utils.Exec(t, conn, "delete from t3 where id6 in (4, 5)")
   407  	qr = utils.Exec(t, conn, "select * from t3")
   408  	require.Empty(t, qr.Rows)
   409  	qr = utils.Exec(t, conn, "select * from t3_id7_idx")
   410  	require.Empty(t, qr.Rows)
   411  }
   412  
   413  func TestConsistentLookupMultiInsert(t *testing.T) {
   414  	conn, closer := start(t)
   415  	defer closer()
   416  	// conn2 is for queries that target shards.
   417  	conn2, err := mysql.Connect(context.Background(), &vtParams)
   418  	require.Nil(t, err)
   419  	defer conn2.Close()
   420  
   421  	utils.Exec(t, conn, "begin")
   422  	utils.Exec(t, conn, "insert into t1(id1, id2) values(1,4), (2,5)")
   423  	utils.Exec(t, conn, "commit")
   424  	qr := utils.Exec(t, conn, "select * from t1")
   425  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)] [INT64(2) INT64(5)]]"; got != want {
   426  		t.Errorf("select:\n%v want\n%v", got, want)
   427  	}
   428  	qr = utils.Exec(t, conn, "select count(*) from t1_id2_idx")
   429  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(2)]]"; got != want {
   430  		t.Errorf("select:\n%v want\n%v", got, want)
   431  	}
   432  
   433  	// Delete one row but leave its lookup dangling.
   434  	utils.Exec(t, conn2, "use `ks:-80`")
   435  	utils.Exec(t, conn2, "delete from t1 where id1=1")
   436  	// Insert a bogus lookup row.
   437  	utils.Exec(t, conn, "insert into t1_id2_idx(id2, keyspace_id) values(6, 'aaa')")
   438  	// Insert 3 rows:
   439  	// first row will insert without changing lookup.
   440  	// second will insert and change lookup.
   441  	// third will be a fresh insert for main and lookup.
   442  	utils.Exec(t, conn, "begin")
   443  	utils.Exec(t, conn, "insert into t1(id1, id2) values(1,2), (3,6), (4,7)")
   444  	utils.Exec(t, conn, "commit")
   445  	qr = utils.Exec(t, conn, "select id1, id2 from t1 order by id1")
   446  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(2)] [INT64(2) INT64(5)] [INT64(3) INT64(6)] [INT64(4) INT64(7)]]"; got != want {
   447  		t.Errorf("select:\n%v want\n%v", got, want)
   448  	}
   449  	qr = utils.Exec(t, conn, "select * from t1_id2_idx where id2=6")
   450  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(6) VARBINARY(\"N\\xb1\\x90ΙΆ\\xfa\\x16\\x9c\")]]"; got != want {
   451  		t.Errorf("select:\n%v want\n%v", got, want)
   452  	}
   453  	qr = utils.Exec(t, conn, "select count(*) from t1_id2_idx")
   454  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(5)]]"; got != want {
   455  		t.Errorf("select:\n%v want\n%v", got, want)
   456  	}
   457  }
   458  
   459  func TestHashLookupMultiInsertIgnore(t *testing.T) {
   460  	conn, closer := start(t)
   461  	defer closer()
   462  	// conn2 is for queries that target shards.
   463  	conn2, err := mysql.Connect(context.Background(), &vtParams)
   464  	require.Nil(t, err)
   465  	defer conn2.Close()
   466  
   467  	utils.Exec(t, conn, "delete from t2")
   468  	utils.Exec(t, conn, "delete from t2_id4_idx")
   469  	defer func() {
   470  		utils.Exec(t, conn, "delete from t2")
   471  		utils.Exec(t, conn, "delete from t2_id4_idx")
   472  	}()
   473  
   474  	// DB should start out clean
   475  	utils.AssertMatches(t, conn, "select count(*) from t2_id4_idx", "[[INT64(0)]]")
   476  	utils.AssertMatches(t, conn, "select count(*) from t2", "[[INT64(0)]]")
   477  
   478  	// Try inserting a bunch of ids at once
   479  	utils.Exec(t, conn, "begin")
   480  	utils.Exec(t, conn, "insert ignore into t2(id3, id4) values(50,60), (30,40), (10,20)")
   481  	utils.Exec(t, conn, "commit")
   482  
   483  	// Verify
   484  	utils.AssertMatches(t, conn, "select id3, id4 from t2 order by id3", "[[INT64(10) INT64(20)] [INT64(30) INT64(40)] [INT64(50) INT64(60)]]")
   485  	utils.AssertMatches(t, conn, "select id3, id4 from t2_id4_idx order by id3", "[[INT64(10) INT64(20)] [INT64(30) INT64(40)] [INT64(50) INT64(60)]]")
   486  }
   487  
   488  func TestConsistentLookupUpdate(t *testing.T) {
   489  	conn, closer := start(t)
   490  	defer closer()
   491  
   492  	/* Simple insert. after this dml, the tables will contain the following:
   493  	t4 (id1, id2):
   494  	1 2
   495  	2 2
   496  	3 3
   497  	4 3
   498  
   499  	t4_id2_idx (id2, id1, keyspace_id:id1):
   500  	2 1 1
   501  	2 2 2
   502  	3 3 3
   503  	3 4 4
   504  	*/
   505  	utils.Exec(t, conn, "insert into t4(id1, id2) values(1, '2'), (2, '2'), (3, '3'), (4, '3')")
   506  	qr := utils.Exec(t, conn, "select id1, id2 from t4 order by id1")
   507  	if got, want := fmt.Sprintf("%v", qr.Rows), `[[INT64(1) VARCHAR("2")] [INT64(2) VARCHAR("2")] [INT64(3) VARCHAR("3")] [INT64(4) VARCHAR("3")]]`; got != want {
   508  		t.Errorf("select:\n%v want\n%v", got, want)
   509  	}
   510  
   511  	/* Updating a lookup column. after this dml, the tables will contain the following:
   512  	t4 (id1, id2):
   513  	1 42
   514  	2 2
   515  	3 3
   516  	4 3
   517  
   518  	t4_id2_idx (id2, id1, keyspace_id:id1):
   519  	42 1 1
   520  	2 2 2
   521  	3 3 3
   522  	3 4 4
   523  	*/
   524  	utils.Exec(t, conn, "update t4 a set a.id2 = '42' where a.id1 = 1")
   525  	qr = utils.Exec(t, conn, "select id1, id2 from t4 order by id1")
   526  	if got, want := fmt.Sprintf("%v", qr.Rows), `[[INT64(1) VARCHAR("42")] [INT64(2) VARCHAR("2")] [INT64(3) VARCHAR("3")] [INT64(4) VARCHAR("3")]]`; got != want {
   527  		t.Errorf("select:\n%v want\n%v", got, want)
   528  	}
   529  
   530  	/* delete one specific keyspace id. after this dml, the tables will contain the following:
   531  	t4 (id1, id2):
   532  	2 2
   533  	3 3
   534  	4 3
   535  
   536  	t4_id2_idx (id2, id1, keyspace_id:id1):
   537  	2 2 2
   538  	3 3 3
   539  	3 4 4
   540  	*/
   541  	utils.Exec(t, conn, "delete from t4 where id2 = '42'")
   542  	qr = utils.Exec(t, conn, "select * from t4 where id2 = '42'")
   543  	require.Empty(t, qr.Rows)
   544  	qr = utils.Exec(t, conn, "select * from t4_id2_idx where id2 = '42'")
   545  	require.Empty(t, qr.Rows)
   546  
   547  	// delete all the rows.
   548  	utils.Exec(t, conn, "delete from t4")
   549  	qr = utils.Exec(t, conn, "select * from t4")
   550  	require.Empty(t, qr.Rows)
   551  	qr = utils.Exec(t, conn, "select * from t4_id2_idx")
   552  	require.Empty(t, qr.Rows)
   553  }
   554  
   555  func TestSelectNullLookup(t *testing.T) {
   556  	conn, closer := start(t)
   557  	defer closer()
   558  
   559  	utils.Exec(t, conn, "insert into t6(id1, id2) values(1, 'a'), (2, 'b'), (3, null)")
   560  
   561  	for _, workload := range []string{"oltp", "olap"} {
   562  		t.Run(workload, func(t *testing.T) {
   563  			utils.Exec(t, conn, "set workload = "+workload)
   564  			utils.AssertMatches(t, conn, "select id1, id2 from t6 order by id1", "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")] [INT64(3) NULL]]")
   565  			utils.AssertIsEmpty(t, conn, "select id1, id2 from t6 where id2 = null")
   566  			utils.AssertMatches(t, conn, "select id1, id2 from t6 where id2 is null", "[[INT64(3) NULL]]")
   567  			utils.AssertMatches(t, conn, "select id1, id2 from t6 where id2 is not null order by id1", "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")]]")
   568  			utils.AssertIsEmpty(t, conn, "select id1, id2 from t6 where id1 IN (null)")
   569  			utils.AssertMatches(t, conn, "select id1, id2 from t6 where id1 IN (1,2,null) order by id1", "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")]]")
   570  			utils.AssertIsEmpty(t, conn, "select id1, id2 from t6 where id1 NOT IN (1,null) order by id1")
   571  			utils.AssertMatches(t, conn, "select id1, id2 from t6 where id1 NOT IN (1,3)", "[[INT64(2) VARCHAR(\"b\")]]")
   572  		})
   573  	}
   574  }
   575  
   576  func TestUnicodeLooseMD5CaseInsensitive(t *testing.T) {
   577  	conn, closer := start(t)
   578  	defer closer()
   579  
   580  	utils.Exec(t, conn, "insert into t4(id1, id2) values(1, 'test')")
   581  
   582  	utils.AssertMatches(t, conn, "SELECT id1, id2 from t4 where id2 = 'Test'", `[[INT64(1) VARCHAR("test")]]`)
   583  }