vitess.io/vitess@v0.16.2/go/vt/vtctl/workflow/vexec/query_plan_test.go (about)

     1  /*
     2  Copyright 2021 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 vexec
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"testing"
    23  
    24  	"vitess.io/vitess/go/test/utils"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  
    28  	"vitess.io/vitess/go/vt/sqlparser"
    29  	"vitess.io/vitess/go/vt/topo"
    30  	"vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil"
    31  
    32  	querypb "vitess.io/vitess/go/vt/proto/query"
    33  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    34  )
    35  
    36  func TestQueryPlanExecute(t *testing.T) {
    37  	t.Parallel()
    38  
    39  	tests := []struct {
    40  		name      string
    41  		plan      FixedQueryPlan
    42  		target    *topo.TabletInfo
    43  		expected  *querypb.QueryResult
    44  		shouldErr bool
    45  		errKind   error
    46  	}{
    47  		{
    48  			name: "success",
    49  			plan: FixedQueryPlan{
    50  				ParsedQuery: &sqlparser.ParsedQuery{
    51  					Query: "SELECT id FROM _vt.vreplication",
    52  				},
    53  				tmc: &testutil.TabletManagerClient{
    54  					VReplicationExecResults: map[string]map[string]struct {
    55  						Result *querypb.QueryResult
    56  						Error  error
    57  					}{
    58  						"zone1-0000000100": {
    59  							"select id from _vt.vreplication": {
    60  								Result: &querypb.QueryResult{
    61  									RowsAffected: 1,
    62  								},
    63  							},
    64  						},
    65  					},
    66  				},
    67  			},
    68  			target: &topo.TabletInfo{
    69  				Tablet: &topodatapb.Tablet{
    70  					Alias: &topodatapb.TabletAlias{
    71  						Cell: "zone1",
    72  						Uid:  100,
    73  					},
    74  				},
    75  			},
    76  			expected: &querypb.QueryResult{
    77  				RowsAffected: 1,
    78  			},
    79  			shouldErr: false,
    80  		},
    81  		{
    82  			name: "no rows affected",
    83  			plan: FixedQueryPlan{
    84  				ParsedQuery: &sqlparser.ParsedQuery{
    85  					Query: "SELECT id FROM _vt.vreplication",
    86  				},
    87  				tmc: &testutil.TabletManagerClient{
    88  					VReplicationExecResults: map[string]map[string]struct {
    89  						Result *querypb.QueryResult
    90  						Error  error
    91  					}{
    92  						"zone1-0000000100": {
    93  							"select id from _vt.vreplication": {
    94  								Result: &querypb.QueryResult{
    95  									RowsAffected: 0,
    96  								},
    97  							},
    98  						},
    99  					},
   100  				},
   101  			},
   102  			target: &topo.TabletInfo{
   103  				Tablet: &topodatapb.Tablet{
   104  					Alias: &topodatapb.TabletAlias{
   105  						Cell: "zone1",
   106  						Uid:  100,
   107  					},
   108  				},
   109  			},
   110  			expected: &querypb.QueryResult{
   111  				RowsAffected: 0,
   112  			},
   113  			shouldErr: false,
   114  		},
   115  		{
   116  			name: "error",
   117  			plan: FixedQueryPlan{
   118  				ParsedQuery: &sqlparser.ParsedQuery{
   119  					Query: "SELECT id FROM _vt.vreplication",
   120  				},
   121  				tmc: &testutil.TabletManagerClient{
   122  					VReplicationExecResults: map[string]map[string]struct {
   123  						Result *querypb.QueryResult
   124  						Error  error
   125  					}{
   126  						"zone1-0000000100": {
   127  							"select id from _vt.vreplication": {
   128  								Error: assert.AnError,
   129  							},
   130  						},
   131  					},
   132  				},
   133  			},
   134  			target: &topo.TabletInfo{
   135  				Tablet: &topodatapb.Tablet{
   136  					Alias: &topodatapb.TabletAlias{
   137  						Cell: "zone1",
   138  						Uid:  100,
   139  					},
   140  				},
   141  			},
   142  			expected:  nil,
   143  			shouldErr: true,
   144  		},
   145  		{
   146  			name: "unprepared query",
   147  			plan: FixedQueryPlan{
   148  				ParsedQuery: nil,
   149  			},
   150  			shouldErr: true,
   151  			errKind:   ErrUnpreparedQuery,
   152  		},
   153  	}
   154  
   155  	for _, tt := range tests {
   156  		tt := tt
   157  
   158  		t.Run(tt.name, func(t *testing.T) {
   159  			t.Parallel()
   160  
   161  			ctx := context.Background()
   162  
   163  			qr, err := tt.plan.Execute(ctx, tt.target)
   164  			if tt.shouldErr {
   165  				assert.Error(t, err)
   166  
   167  				if tt.errKind != nil {
   168  					assert.True(t, errors.Is(err, tt.errKind), "expected error kind (= %v), got = %v", tt.errKind, err)
   169  				}
   170  
   171  				return
   172  			}
   173  
   174  			assert.NoError(t, err)
   175  			utils.MustMatch(t, tt.expected, qr)
   176  		})
   177  	}
   178  }
   179  
   180  func TestQueryPlanExecuteScatter(t *testing.T) {
   181  	t.Parallel()
   182  
   183  	tests := []struct {
   184  		name    string
   185  		plan    FixedQueryPlan
   186  		targets []*topo.TabletInfo
   187  		// This is different from our actual return type because guaranteeing
   188  		// exact pointers in this table-driven style is a bit tough.
   189  		expected  map[string]*querypb.QueryResult
   190  		shouldErr bool
   191  		errKind   error
   192  	}{
   193  		{
   194  			name: "success",
   195  			plan: FixedQueryPlan{
   196  				ParsedQuery: &sqlparser.ParsedQuery{
   197  					Query: "SELECT id FROM _vt.vreplication",
   198  				},
   199  				tmc: &testutil.TabletManagerClient{
   200  					VReplicationExecResults: map[string]map[string]struct {
   201  						Result *querypb.QueryResult
   202  						Error  error
   203  					}{
   204  						"zone1-0000000100": {
   205  							"select id from _vt.vreplication": {
   206  								Result: &querypb.QueryResult{
   207  									RowsAffected: 10,
   208  								},
   209  							},
   210  						},
   211  						"zone1-0000000101": {
   212  							"select id from _vt.vreplication": {
   213  								Result: &querypb.QueryResult{
   214  									RowsAffected: 5,
   215  								},
   216  							},
   217  						},
   218  					},
   219  				},
   220  			},
   221  			targets: []*topo.TabletInfo{
   222  				{
   223  					Tablet: &topodatapb.Tablet{
   224  						Alias: &topodatapb.TabletAlias{
   225  							Cell: "zone1",
   226  							Uid:  100,
   227  						},
   228  					},
   229  				},
   230  				{
   231  					Tablet: &topodatapb.Tablet{
   232  						Alias: &topodatapb.TabletAlias{
   233  							Cell: "zone1",
   234  							Uid:  101,
   235  						},
   236  					},
   237  				},
   238  			},
   239  			expected: map[string]*querypb.QueryResult{
   240  				"zone1-0000000100": {
   241  					RowsAffected: 10,
   242  				},
   243  				"zone1-0000000101": {
   244  					RowsAffected: 5,
   245  				},
   246  			},
   247  			shouldErr: false,
   248  		},
   249  		{
   250  			name: "some targets fail",
   251  			plan: FixedQueryPlan{
   252  				ParsedQuery: &sqlparser.ParsedQuery{
   253  					Query: "SELECT id FROM _vt.vreplication",
   254  				},
   255  				tmc: &testutil.TabletManagerClient{
   256  					VReplicationExecResults: map[string]map[string]struct {
   257  						Result *querypb.QueryResult
   258  						Error  error
   259  					}{
   260  						"zone1-0000000100": {
   261  							"select id from _vt.vreplication": {
   262  								Error: assert.AnError,
   263  							},
   264  						},
   265  						"zone1-0000000101": {
   266  							"select id from _vt.vreplication": {
   267  								Result: &querypb.QueryResult{
   268  									RowsAffected: 5,
   269  								},
   270  							},
   271  						},
   272  					},
   273  				},
   274  			},
   275  			targets: []*topo.TabletInfo{
   276  				{
   277  					Tablet: &topodatapb.Tablet{
   278  						Alias: &topodatapb.TabletAlias{
   279  							Cell: "zone1",
   280  							Uid:  100,
   281  						},
   282  					},
   283  				},
   284  				{
   285  					Tablet: &topodatapb.Tablet{
   286  						Alias: &topodatapb.TabletAlias{
   287  							Cell: "zone1",
   288  							Uid:  101,
   289  						},
   290  					},
   291  				},
   292  			},
   293  			shouldErr: true,
   294  		},
   295  		{
   296  			name: "unprepared query",
   297  			plan: FixedQueryPlan{
   298  				ParsedQuery: nil,
   299  			},
   300  			shouldErr: true,
   301  			errKind:   ErrUnpreparedQuery,
   302  		},
   303  	}
   304  
   305  	for _, tt := range tests {
   306  		tt := tt
   307  
   308  		t.Run(tt.name, func(t *testing.T) {
   309  			t.Parallel()
   310  
   311  			ctx := context.Background()
   312  
   313  			results, err := tt.plan.ExecuteScatter(ctx, tt.targets...)
   314  			if tt.shouldErr {
   315  				assert.Error(t, err)
   316  
   317  				if tt.errKind != nil {
   318  					assert.True(t, errors.Is(err, tt.errKind), "expected error kind (= %v), got = %v", tt.errKind, err)
   319  				}
   320  
   321  				return
   322  			}
   323  
   324  			assert.NoError(t, err)
   325  
   326  			resultsByAlias := make(map[string]*querypb.QueryResult, len(results))
   327  			for tablet, qr := range results {
   328  				resultsByAlias[tablet.AliasString()] = qr
   329  			}
   330  
   331  			utils.MustMatch(t, tt.expected, resultsByAlias)
   332  		})
   333  	}
   334  }