vitess.io/vitess@v0.16.2/go/vt/vtgate/endtoend/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 endtoend
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	"github.com/stretchr/testify/require"
    27  
    28  	"vitess.io/vitess/go/mysql"
    29  	"vitess.io/vitess/go/sqltypes"
    30  )
    31  
    32  func TestConsistentLookup(t *testing.T) {
    33  	ctx := context.Background()
    34  	conn, err := mysql.Connect(ctx, &vtParams)
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	defer conn.Close()
    39  	// conn2 is for queries that target shards.
    40  	conn2, err := mysql.Connect(ctx, &vtParams)
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	defer conn2.Close()
    45  
    46  	// Simple insert.
    47  	exec(t, conn, "begin")
    48  	exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
    49  	exec(t, conn, "commit")
    50  	qr := exec(t, conn, "select * from t1")
    51  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
    52  		t.Errorf("select:\n%v want\n%v", got, want)
    53  	}
    54  	qr = exec(t, conn, "select * from t1_id2_idx")
    55  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
    56  		t.Errorf("select:\n%v want\n%v", got, want)
    57  	}
    58  
    59  	// Inserting again should fail.
    60  	exec(t, conn, "begin")
    61  	_, err = conn.ExecuteFetch("insert into t1(id1, id2) values(1, 4)", 1000, false)
    62  	exec(t, conn, "rollback")
    63  	require.Error(t, err)
    64  	mysqlErr := err.(*mysql.SQLError)
    65  	assert.Equal(t, 1062, mysqlErr.Num)
    66  	assert.Equal(t, "23000", mysqlErr.State)
    67  
    68  	// Simple delete.
    69  	exec(t, conn, "begin")
    70  	exec(t, conn, "delete from t1 where id1=1")
    71  	exec(t, conn, "commit")
    72  	qr = exec(t, conn, "select * from t1")
    73  	if got, want := fmt.Sprintf("%v", qr.Rows), "[]"; got != want {
    74  		t.Errorf("select:\n%v want\n%v", got, want)
    75  	}
    76  	qr = exec(t, conn, "select * from t1_id2_idx")
    77  	if got, want := fmt.Sprintf("%v", qr.Rows), "[]"; got != want {
    78  		t.Errorf("select:\n%v want\n%v", got, want)
    79  	}
    80  
    81  	// Autocommit insert.
    82  	exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
    83  	qr = exec(t, conn, "select * from t1")
    84  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
    85  		t.Errorf("select:\n%v want\n%v", got, want)
    86  	}
    87  	qr = exec(t, conn, "select id2 from t1_id2_idx")
    88  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4)]]"; got != want {
    89  		t.Errorf("select:\n%v want\n%v", got, want)
    90  	}
    91  	// Autocommit delete.
    92  	exec(t, conn, "delete from t1 where id1=1")
    93  
    94  	// Dangling row pointing to existing keyspace id.
    95  	exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
    96  	// Delete the main row only.
    97  	exec(t, conn2, "use `ks:-80`")
    98  	exec(t, conn2, "delete from t1 where id1=1")
    99  	// Verify the lookup row is still there.
   100  	qr = exec(t, conn, "select id2 from t1_id2_idx")
   101  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4)]]"; got != want {
   102  		t.Errorf("select:\n%v want\n%v", got, want)
   103  	}
   104  	// Insert should still succeed.
   105  	exec(t, conn, "begin")
   106  	exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
   107  	exec(t, conn, "commit")
   108  	qr = exec(t, conn, "select * from t1")
   109  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
   110  		t.Errorf("select:\n%v want\n%v", got, want)
   111  	}
   112  	// Lookup row should be unchanged.
   113  	qr = exec(t, conn, "select * from t1_id2_idx")
   114  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   115  		t.Errorf("select:\n%v want\n%v", got, want)
   116  	}
   117  
   118  	// Dangling row not pointing to existing keyspace id.
   119  	exec(t, conn2, "use `ks:-80`")
   120  	exec(t, conn2, "delete from t1 where id1=1")
   121  	// Update the lookup row with bogus keyspace id.
   122  	exec(t, conn, "update t1_id2_idx set keyspace_id='aaa' where id2=4")
   123  	qr = exec(t, conn, "select * from t1_id2_idx")
   124  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"aaa\")]]"; got != want {
   125  		t.Errorf("select:\n%v want\n%v", got, want)
   126  	}
   127  	// Insert should still succeed.
   128  	exec(t, conn, "begin")
   129  	exec(t, conn, "insert into t1(id1, id2) values(1, 4)")
   130  	exec(t, conn, "commit")
   131  	qr = exec(t, conn, "select * from t1")
   132  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
   133  		t.Errorf("select:\n%v want\n%v", got, want)
   134  	}
   135  	// lookup row must be updated.
   136  	qr = exec(t, conn, "select * from t1_id2_idx")
   137  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   138  		t.Errorf("select:\n%v want\n%v", got, want)
   139  	}
   140  
   141  	// Update, but don't change anything. This should not deadlock.
   142  	exec(t, conn, "begin")
   143  	exec(t, conn, "update t1 set id2=4 where id1=1")
   144  	exec(t, conn, "commit")
   145  	qr = exec(t, conn, "select * from t1")
   146  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)]]"; got != want {
   147  		t.Errorf("select:\n%v want\n%v", got, want)
   148  	}
   149  	qr = exec(t, conn, "select * from t1_id2_idx")
   150  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(4) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   151  		t.Errorf("select:\n%v want\n%v", got, want)
   152  	}
   153  
   154  	// Update, and change the lookup value. This should change main and lookup rows.
   155  	exec(t, conn, "begin")
   156  	exec(t, conn, "update t1 set id2=5 where id1=1")
   157  	exec(t, conn, "commit")
   158  	qr = exec(t, conn, "select * from t1")
   159  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(5)]]"; got != want {
   160  		t.Errorf("select:\n%v want\n%v", got, want)
   161  	}
   162  	qr = exec(t, conn, "select * from t1_id2_idx")
   163  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(5) VARBINARY(\"\\x16k@\\xb4J\\xbaK\\xd6\")]]"; got != want {
   164  		t.Errorf("select:\n%v want\n%v", got, want)
   165  	}
   166  	exec(t, conn, "delete from t1 where id1=1")
   167  }
   168  
   169  func TestConsistentLookupMultiInsert(t *testing.T) {
   170  	ctx := context.Background()
   171  	conn, err := mysql.Connect(ctx, &vtParams)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  	defer conn.Close()
   176  	// conn2 is for queries that target shards.
   177  	conn2, err := mysql.Connect(ctx, &vtParams)
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	defer conn2.Close()
   182  
   183  	exec(t, conn, "begin")
   184  	exec(t, conn, "insert into t1(id1, id2) values(1,4), (2,5)")
   185  	exec(t, conn, "commit")
   186  	qr := exec(t, conn, "select * from t1")
   187  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(4)] [INT64(2) INT64(5)]]"; got != want {
   188  		t.Errorf("select:\n%v want\n%v", got, want)
   189  	}
   190  	qr = exec(t, conn, "select count(*) from t1_id2_idx")
   191  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(2)]]"; got != want {
   192  		t.Errorf("select:\n%v want\n%v", got, want)
   193  	}
   194  
   195  	// Delete one row but leave its lookup dangling.
   196  	exec(t, conn2, "use `ks:-80`")
   197  	exec(t, conn2, "delete from t1 where id1=1")
   198  	// Insert a bogus lookup row.
   199  	exec(t, conn, "insert into t1_id2_idx(id2, keyspace_id) values(6, 'aaa')")
   200  	// Insert 3 rows:
   201  	// first row will insert without changing lookup.
   202  	// second will insert and change lookup.
   203  	// third will be a fresh insert for main and lookup.
   204  	exec(t, conn, "begin")
   205  	exec(t, conn, "insert into t1(id1, id2) values(1,2), (3,6), (4,7)")
   206  	exec(t, conn, "commit")
   207  	qr = exec(t, conn, "select id1, id2 from t1 order by id1")
   208  	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 {
   209  		t.Errorf("select:\n%v want\n%v", got, want)
   210  	}
   211  	qr = exec(t, conn, "select * from t1_id2_idx where id2=6")
   212  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(6) VARBINARY(\"N\\xb1\\x90ΙΆ\\xfa\\x16\\x9c\")]]"; got != want {
   213  		t.Errorf("select:\n%v want\n%v", got, want)
   214  	}
   215  	qr = exec(t, conn, "select count(*) from t1_id2_idx")
   216  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(5)]]"; got != want {
   217  		t.Errorf("select:\n%v want\n%v", got, want)
   218  	}
   219  	exec(t, conn, "delete from t1 where id1=1")
   220  	exec(t, conn, "delete from t1 where id1=2")
   221  	exec(t, conn, "delete from t1 where id1=3")
   222  	exec(t, conn, "delete from t1 where id1=4")
   223  	exec(t, conn, "delete from t1_id2_idx where id2=4")
   224  }
   225  
   226  func TestLookupMultiInsertIgnore(t *testing.T) {
   227  	ctx := context.Background()
   228  	conn, err := mysql.Connect(ctx, &vtParams)
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  	defer conn.Close()
   233  	// conn2 is for queries that target shards.
   234  	conn2, err := mysql.Connect(ctx, &vtParams)
   235  	if err != nil {
   236  		t.Fatal(err)
   237  	}
   238  	defer conn2.Close()
   239  
   240  	// DB should start out clean
   241  	qr := exec(t, conn, "select count(*) from t2_id4_idx")
   242  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(0)]]"; got != want {
   243  		t.Errorf("select:\n%v want\n%v", got, want)
   244  	}
   245  	qr = exec(t, conn, "select count(*) from t2")
   246  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(0)]]"; got != want {
   247  		t.Errorf("select:\n%v want\n%v", got, want)
   248  	}
   249  
   250  	// Try inserting a bunch of ids at once
   251  	exec(t, conn, "begin")
   252  	exec(t, conn, "insert ignore into t2(id3, id4) values(50,60), (30,40), (10,20)")
   253  	exec(t, conn, "commit")
   254  
   255  	// Verify
   256  	qr = exec(t, conn, "select id3, id4 from t2 order by id3")
   257  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(10) INT64(20)] [INT64(30) INT64(40)] [INT64(50) INT64(60)]]"; got != want {
   258  		t.Errorf("select:\n%v want\n%v", got, want)
   259  	}
   260  	qr = exec(t, conn, "select id3, id4 from t2_id4_idx order by id3")
   261  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(10) INT64(20)] [INT64(30) INT64(40)] [INT64(50) INT64(60)]]"; got != want {
   262  		t.Errorf("select:\n%v want\n%v", got, want)
   263  	}
   264  }
   265  
   266  func TestConsistentLookupMultiInsertIgnore(t *testing.T) {
   267  	ctx := context.Background()
   268  	conn, err := mysql.Connect(ctx, &vtParams)
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	defer conn.Close()
   273  	// conn2 is for queries that target shards.
   274  	conn2, err := mysql.Connect(ctx, &vtParams)
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	defer conn2.Close()
   279  
   280  	// DB should start out clean
   281  	qr := exec(t, conn, "select count(*) from t1_id2_idx")
   282  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(0)]]"; got != want {
   283  		t.Errorf("select:\n%v want\n%v", got, want)
   284  	}
   285  	qr = exec(t, conn, "select count(*) from t1")
   286  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(0)]]"; got != want {
   287  		t.Errorf("select:\n%v want\n%v", got, want)
   288  	}
   289  
   290  	// Try inserting a bunch of ids at once
   291  	exec(t, conn, "begin")
   292  	exec(t, conn, "insert ignore into t1(id1, id2) values(50,60), (30,40), (10,20)")
   293  	exec(t, conn, "commit")
   294  
   295  	// Verify
   296  	qr = exec(t, conn, "select id1, id2 from t1 order by id1")
   297  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(10) INT64(20)] [INT64(30) INT64(40)] [INT64(50) INT64(60)]]"; got != want {
   298  		t.Errorf("select:\n%v want\n%v", got, want)
   299  	}
   300  	qr = exec(t, conn, "select id2 from t1_id2_idx order by id2")
   301  	if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(20)] [INT64(40)] [INT64(60)]]"; got != want {
   302  		t.Errorf("select:\n%v want\n%v", got, want)
   303  	}
   304  }
   305  
   306  func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result {
   307  	t.Helper()
   308  	qr, err := conn.ExecuteFetch(query, 1000, true)
   309  	if err != nil {
   310  		t.Fatal(err)
   311  	}
   312  	return qr
   313  }