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 }