vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/send_test.go (about)

     1  /*
     2  Copyright 2020 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 engine
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"testing"
    23  
    24  	"vitess.io/vitess/go/sqltypes"
    25  
    26  	"github.com/stretchr/testify/require"
    27  
    28  	"vitess.io/vitess/go/vt/key"
    29  	querypb "vitess.io/vitess/go/vt/proto/query"
    30  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    31  )
    32  
    33  func TestSendTable(t *testing.T) {
    34  	type testCase struct {
    35  		testName             string
    36  		sharded              bool
    37  		shards               []string
    38  		destination          key.Destination
    39  		expectedQueryLog     []string
    40  		expectedError        string
    41  		isDML                bool
    42  		singleShardOnly      bool
    43  		multiShardAutocommit bool
    44  	}
    45  
    46  	singleShard := []string{"0"}
    47  	twoShards := []string{"-20", "20-"}
    48  	tests := []testCase{
    49  		{
    50  			testName:    "unsharded with no autocommit",
    51  			sharded:     false,
    52  			shards:      singleShard,
    53  			destination: key.DestinationAllShards{},
    54  			expectedQueryLog: []string{
    55  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
    56  				`ExecuteMultiShard ks.0: dummy_query {} false false`,
    57  			},
    58  			isDML:                false,
    59  			multiShardAutocommit: false,
    60  		},
    61  		{
    62  			testName:    "sharded with no autocommit",
    63  			sharded:     true,
    64  			shards:      twoShards,
    65  			destination: key.DestinationShard("20-"),
    66  			expectedQueryLog: []string{
    67  				`ResolveDestinations ks [] Destinations:DestinationShard(20-)`,
    68  				`ExecuteMultiShard ks.DestinationShard(20-): dummy_query {} false false`,
    69  			},
    70  			isDML:                false,
    71  			multiShardAutocommit: false,
    72  		},
    73  		{
    74  			testName:    "unsharded",
    75  			sharded:     false,
    76  			shards:      singleShard,
    77  			destination: key.DestinationAllShards{},
    78  			expectedQueryLog: []string{
    79  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
    80  				`ExecuteMultiShard ks.0: dummy_query {} true true`,
    81  			},
    82  			isDML:                true,
    83  			multiShardAutocommit: false,
    84  		},
    85  		{
    86  			testName:    "sharded with single shard destination",
    87  			sharded:     true,
    88  			shards:      twoShards,
    89  			destination: key.DestinationShard("20-"),
    90  			expectedQueryLog: []string{
    91  				`ResolveDestinations ks [] Destinations:DestinationShard(20-)`,
    92  				`ExecuteMultiShard ks.DestinationShard(20-): dummy_query {} true true`,
    93  			},
    94  			isDML:                true,
    95  			multiShardAutocommit: false,
    96  		},
    97  		{
    98  			testName:    "sharded with multi shard destination",
    99  			sharded:     true,
   100  			shards:      twoShards,
   101  			destination: key.DestinationAllShards{},
   102  			expectedQueryLog: []string{
   103  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   104  				`ExecuteMultiShard ks.-20: dummy_query {} ks.20-: dummy_query {} true false`,
   105  			},
   106  			isDML:                true,
   107  			multiShardAutocommit: false,
   108  		},
   109  		{
   110  			testName:    "sharded with multi shard destination and autocommit",
   111  			sharded:     true,
   112  			shards:      twoShards,
   113  			destination: key.DestinationAllShards{},
   114  			expectedQueryLog: []string{
   115  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   116  				`ExecuteMultiShard ks.-20: dummy_query {} ks.20-: dummy_query {} true true`,
   117  			},
   118  			isDML:                true,
   119  			multiShardAutocommit: true,
   120  		},
   121  		{
   122  			testName:    "sharded with multi shard destination",
   123  			sharded:     true,
   124  			shards:      twoShards,
   125  			destination: key.DestinationAllShards{},
   126  			expectedQueryLog: []string{
   127  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   128  			},
   129  			expectedError:        "Unexpected error, DestinationKeyspaceID mapping to multiple shards: dummy_query, got: DestinationAllShards()",
   130  			isDML:                true,
   131  			singleShardOnly:      true,
   132  			multiShardAutocommit: false,
   133  		},
   134  	}
   135  
   136  	for _, tc := range tests {
   137  		t.Run(tc.testName, func(t *testing.T) {
   138  			send := &Send{
   139  				Keyspace: &vindexes.Keyspace{
   140  					Name:    "ks",
   141  					Sharded: tc.sharded,
   142  				},
   143  				Query:                "dummy_query",
   144  				TargetDestination:    tc.destination,
   145  				IsDML:                tc.isDML,
   146  				SingleShardOnly:      tc.singleShardOnly,
   147  				MultishardAutocommit: tc.multiShardAutocommit,
   148  			}
   149  			vc := &loggingVCursor{shards: tc.shards}
   150  			_, err := send.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false)
   151  			if tc.expectedError != "" {
   152  				require.EqualError(t, err, tc.expectedError)
   153  			} else {
   154  				require.NoError(t, err)
   155  			}
   156  			vc.ExpectLog(t, tc.expectedQueryLog)
   157  
   158  			// Failure cases
   159  			vc = &loggingVCursor{shardErr: errors.New("shard_error")}
   160  			_, err = send.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false)
   161  			require.EqualError(t, err, "shard_error")
   162  
   163  			if !tc.sharded {
   164  				vc = &loggingVCursor{}
   165  				_, err = send.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false)
   166  				require.EqualError(t, err, "Keyspace does not have exactly one shard: []")
   167  			}
   168  		})
   169  	}
   170  }
   171  
   172  func TestSendTable_StreamExecute(t *testing.T) {
   173  	type testCase struct {
   174  		testName         string
   175  		sharded          bool
   176  		shards           []string
   177  		destination      key.Destination
   178  		expectedQueryLog []string
   179  		expectedError    string
   180  		isDML            bool
   181  		singleShardOnly  bool
   182  	}
   183  
   184  	singleShard := []string{"0"}
   185  	twoShards := []string{"-20", "20-"}
   186  	tests := []testCase{
   187  		{
   188  			testName:    "unsharded with no autocommit",
   189  			sharded:     false,
   190  			shards:      singleShard,
   191  			destination: key.DestinationAllShards{},
   192  			expectedQueryLog: []string{
   193  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   194  				`StreamExecuteMulti dummy_query ks.0: {} `,
   195  			},
   196  			isDML: false,
   197  		},
   198  		{
   199  			testName:    "sharded with no autocommit",
   200  			sharded:     true,
   201  			shards:      twoShards,
   202  			destination: key.DestinationShard("20-"),
   203  			expectedQueryLog: []string{
   204  				`ResolveDestinations ks [] Destinations:DestinationShard(20-)`,
   205  				`StreamExecuteMulti dummy_query ks.DestinationShard(20-): {} `,
   206  			},
   207  			isDML: false,
   208  		},
   209  		{
   210  			testName:    "unsharded",
   211  			sharded:     false,
   212  			shards:      singleShard,
   213  			destination: key.DestinationAllShards{},
   214  			expectedQueryLog: []string{
   215  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   216  				`StreamExecuteMulti dummy_query ks.0: {} `,
   217  			},
   218  			isDML: true,
   219  		},
   220  		{
   221  			testName:    "sharded with single shard destination",
   222  			sharded:     true,
   223  			shards:      twoShards,
   224  			destination: key.DestinationShard("20-"),
   225  			expectedQueryLog: []string{
   226  				`ResolveDestinations ks [] Destinations:DestinationShard(20-)`,
   227  				`StreamExecuteMulti dummy_query ks.DestinationShard(20-): {} `,
   228  			},
   229  			isDML: true,
   230  		},
   231  		{
   232  			testName:    "sharded with multi shard destination",
   233  			sharded:     true,
   234  			shards:      twoShards,
   235  			destination: key.DestinationAllShards{},
   236  			expectedQueryLog: []string{
   237  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   238  				`StreamExecuteMulti dummy_query ks.-20: {} ks.20-: {} `,
   239  			},
   240  			isDML: true,
   241  		},
   242  		{
   243  			testName:    "sharded with multi shard destination single shard setting",
   244  			sharded:     true,
   245  			shards:      twoShards,
   246  			destination: key.DestinationAllShards{},
   247  			expectedQueryLog: []string{
   248  				`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   249  			},
   250  			expectedError:   "Unexpected error, DestinationKeyspaceID mapping to multiple shards: dummy_query, got: DestinationAllShards()",
   251  			isDML:           true,
   252  			singleShardOnly: true,
   253  		},
   254  	}
   255  
   256  	for _, tc := range tests {
   257  		t.Run(tc.testName, func(t *testing.T) {
   258  			send := &Send{
   259  				Keyspace: &vindexes.Keyspace{
   260  					Name:    "ks",
   261  					Sharded: tc.sharded,
   262  				},
   263  				Query:             "dummy_query",
   264  				TargetDestination: tc.destination,
   265  				IsDML:             tc.isDML,
   266  				SingleShardOnly:   tc.singleShardOnly,
   267  			}
   268  			vc := &loggingVCursor{shards: tc.shards}
   269  			_, err := wrapStreamExecute(send, vc, map[string]*querypb.BindVariable{}, false)
   270  			if tc.expectedError != "" {
   271  				require.EqualError(t, err, tc.expectedError)
   272  			} else {
   273  				require.NoError(t, err)
   274  			}
   275  			vc.ExpectLog(t, tc.expectedQueryLog)
   276  
   277  			// Failure cases
   278  			vc = &loggingVCursor{shardErr: errors.New("shard_error")}
   279  			_, err = wrapStreamExecute(send, vc, map[string]*querypb.BindVariable{}, false)
   280  			require.EqualError(t, err, "shard_error")
   281  
   282  			if !tc.sharded {
   283  				vc = &loggingVCursor{}
   284  				_, err = wrapStreamExecute(send, vc, map[string]*querypb.BindVariable{}, false)
   285  				require.EqualError(t, err, "Keyspace does not have exactly one shard: []")
   286  			}
   287  		})
   288  	}
   289  }
   290  
   291  func TestSendGetFields(t *testing.T) {
   292  	results := []*sqltypes.Result{sqltypes.MakeTestResult(
   293  		sqltypes.MakeTestFields(
   294  			"id|c1|c2|c3",
   295  			"int64|int64|int64|int64",
   296  		),
   297  		"1|4|5|6",
   298  		"2|7|8|9",
   299  	)}
   300  
   301  	send := &Send{
   302  		Keyspace: &vindexes.Keyspace{
   303  			Name:    "ks",
   304  			Sharded: true,
   305  		},
   306  		Query:             "dummy_query",
   307  		TargetDestination: key.DestinationAllShards{},
   308  		IsDML:             true,
   309  		SingleShardOnly:   false,
   310  	}
   311  	vc := &loggingVCursor{shards: []string{"-20", "20-"}, results: results}
   312  	qr, err := send.GetFields(context.Background(), vc, map[string]*querypb.BindVariable{})
   313  	require.NoError(t, err)
   314  	vc.ExpectLog(t, []string{
   315  		`ResolveDestinations ks [] Destinations:DestinationAllShards()`,
   316  		`ExecuteMultiShard ks.-20: dummy_query {} ks.20-: dummy_query {} true false`,
   317  	})
   318  	require.Nil(t, qr.Rows)
   319  	require.Equal(t, 4, len(qr.Fields))
   320  }