vitess.io/vitess@v0.16.2/go/sqltypes/bind_variables_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 sqltypes 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 27 "github.com/stretchr/testify/require" 28 "google.golang.org/protobuf/proto" 29 30 querypb "vitess.io/vitess/go/vt/proto/query" 31 ) 32 33 func TestProtoConversions(t *testing.T) { 34 v := TestValue(Int64, "1") 35 got := ValueToProto(v) 36 want := &querypb.Value{Type: Int64, Value: []byte("1")} 37 if !proto.Equal(got, want) { 38 t.Errorf("ValueToProto: %v, want %v", got, want) 39 } 40 gotback := ProtoToValue(got) 41 if !reflect.DeepEqual(gotback, v) { 42 t.Errorf("ProtoToValue: %v, want %v", gotback, v) 43 } 44 } 45 46 func TestBuildBindVariables(t *testing.T) { 47 tcases := []struct { 48 in map[string]any 49 out map[string]*querypb.BindVariable 50 err string 51 }{{ 52 in: nil, 53 out: nil, 54 }, { 55 in: map[string]any{ 56 "k": int64(1), 57 }, 58 out: map[string]*querypb.BindVariable{ 59 "k": Int64BindVariable(1), 60 }, 61 }, { 62 in: map[string]any{ 63 "k": byte(1), 64 }, 65 err: "k: type uint8 not supported as bind var: 1", 66 }} 67 for _, tcase := range tcases { 68 bindVars, err := BuildBindVariables(tcase.in) 69 if err != nil { 70 if err.Error() != tcase.err { 71 t.Errorf("MapToBindVars(%v) error: %v, want %s", tcase.in, err, tcase.err) 72 } 73 continue 74 } 75 if tcase.err != "" { 76 t.Errorf("MapToBindVars(%v) error: nil, want %s", tcase.in, tcase.err) 77 continue 78 } 79 if !BindVariablesEqual(bindVars, tcase.out) { 80 t.Errorf("MapToBindVars(%v): %v, want %s", tcase.in, bindVars, tcase.out) 81 } 82 } 83 } 84 85 func TestBuildBindVariable(t *testing.T) { 86 tcases := []struct { 87 in any 88 out *querypb.BindVariable 89 err string 90 }{{ 91 in: "aa", 92 out: &querypb.BindVariable{ 93 Type: querypb.Type_VARCHAR, 94 Value: []byte("aa"), 95 }, 96 }, { 97 in: []byte("aa"), 98 out: &querypb.BindVariable{ 99 Type: querypb.Type_VARBINARY, 100 Value: []byte("aa"), 101 }, 102 }, { 103 in: true, 104 out: &querypb.BindVariable{ 105 Type: querypb.Type_INT8, 106 Value: []byte("1"), 107 }, 108 }, { 109 in: false, 110 out: &querypb.BindVariable{ 111 Type: querypb.Type_INT8, 112 Value: []byte("0"), 113 }, 114 }, { 115 in: int(1), 116 out: &querypb.BindVariable{ 117 Type: querypb.Type_INT64, 118 Value: []byte("1"), 119 }, 120 }, { 121 in: int64(1), 122 out: &querypb.BindVariable{ 123 Type: querypb.Type_INT64, 124 Value: []byte("1"), 125 }, 126 }, { 127 in: uint64(1), 128 out: &querypb.BindVariable{ 129 Type: querypb.Type_UINT64, 130 Value: []byte("1"), 131 }, 132 }, { 133 in: float64(1), 134 out: &querypb.BindVariable{ 135 Type: querypb.Type_FLOAT64, 136 Value: []byte("1"), 137 }, 138 }, { 139 in: nil, 140 out: NullBindVariable, 141 }, { 142 in: MakeTrusted(Int64, []byte("1")), 143 out: &querypb.BindVariable{ 144 Type: querypb.Type_INT64, 145 Value: []byte("1"), 146 }, 147 }, { 148 in: &querypb.BindVariable{ 149 Type: querypb.Type_INT64, 150 Value: []byte("1"), 151 }, 152 out: &querypb.BindVariable{ 153 Type: querypb.Type_INT64, 154 Value: []byte("1"), 155 }, 156 }, { 157 in: []any{"aa", int64(1)}, 158 out: &querypb.BindVariable{ 159 Type: querypb.Type_TUPLE, 160 Values: []*querypb.Value{{ 161 Type: querypb.Type_VARCHAR, 162 Value: []byte("aa"), 163 }, { 164 Type: querypb.Type_INT64, 165 Value: []byte("1"), 166 }}, 167 }, 168 }, { 169 in: []string{"aa", "bb"}, 170 out: &querypb.BindVariable{ 171 Type: querypb.Type_TUPLE, 172 Values: []*querypb.Value{{ 173 Type: querypb.Type_VARCHAR, 174 Value: []byte("aa"), 175 }, { 176 Type: querypb.Type_VARCHAR, 177 Value: []byte("bb"), 178 }}, 179 }, 180 }, { 181 in: [][]byte{[]byte("aa"), []byte("bb")}, 182 out: &querypb.BindVariable{ 183 Type: querypb.Type_TUPLE, 184 Values: []*querypb.Value{{ 185 Type: querypb.Type_VARBINARY, 186 Value: []byte("aa"), 187 }, { 188 Type: querypb.Type_VARBINARY, 189 Value: []byte("bb"), 190 }}, 191 }, 192 }, { 193 in: []int{1, 2}, 194 out: &querypb.BindVariable{ 195 Type: querypb.Type_TUPLE, 196 Values: []*querypb.Value{{ 197 Type: querypb.Type_INT64, 198 Value: []byte("1"), 199 }, { 200 Type: querypb.Type_INT64, 201 Value: []byte("2"), 202 }}, 203 }, 204 }, { 205 in: []int64{1, 2}, 206 out: &querypb.BindVariable{ 207 Type: querypb.Type_TUPLE, 208 Values: []*querypb.Value{{ 209 Type: querypb.Type_INT64, 210 Value: []byte("1"), 211 }, { 212 Type: querypb.Type_INT64, 213 Value: []byte("2"), 214 }}, 215 }, 216 }, { 217 in: []uint64{1, 2}, 218 out: &querypb.BindVariable{ 219 Type: querypb.Type_TUPLE, 220 Values: []*querypb.Value{{ 221 Type: querypb.Type_UINT64, 222 Value: []byte("1"), 223 }, { 224 Type: querypb.Type_UINT64, 225 Value: []byte("2"), 226 }}, 227 }, 228 }, { 229 in: []float64{1, 2}, 230 out: &querypb.BindVariable{ 231 Type: querypb.Type_TUPLE, 232 Values: []*querypb.Value{{ 233 Type: querypb.Type_FLOAT64, 234 Value: []byte("1"), 235 }, { 236 Type: querypb.Type_FLOAT64, 237 Value: []byte("2"), 238 }}, 239 }, 240 }, { 241 in: byte(1), 242 err: "type uint8 not supported as bind var: 1", 243 }, { 244 in: []any{1, byte(1)}, 245 err: "type uint8 not supported as bind var: 1", 246 }} 247 for _, tcase := range tcases { 248 t.Run(fmt.Sprintf("%v", tcase.in), func(t *testing.T) { 249 bv, err := BuildBindVariable(tcase.in) 250 if tcase.err != "" { 251 require.EqualError(t, err, tcase.err) 252 } else { 253 require.Truef(t, proto.Equal(tcase.out, bv), "binvar output did not match") 254 } 255 }) 256 } 257 } 258 259 func TestValidateBindVarables(t *testing.T) { 260 tcases := []struct { 261 in map[string]*querypb.BindVariable 262 err string 263 }{{ 264 in: map[string]*querypb.BindVariable{ 265 "v": { 266 Type: querypb.Type_INT64, 267 Value: []byte("1"), 268 }, 269 }, 270 err: "", 271 }, { 272 in: map[string]*querypb.BindVariable{ 273 "v": { 274 Type: querypb.Type_INT64, 275 Value: []byte("a"), 276 }, 277 }, 278 err: `v: strconv.ParseInt: parsing "a": invalid syntax`, 279 }, { 280 in: map[string]*querypb.BindVariable{ 281 "v": { 282 Type: querypb.Type_TUPLE, 283 Values: []*querypb.Value{{ 284 Type: Int64, 285 Value: []byte("a"), 286 }}, 287 }, 288 }, 289 err: `v: strconv.ParseInt: parsing "a": invalid syntax`, 290 }} 291 for _, tcase := range tcases { 292 err := ValidateBindVariables(tcase.in) 293 if tcase.err != "" { 294 if err == nil || err.Error() != tcase.err { 295 t.Errorf("ValidateBindVars(%v): %v, want %s", tcase.in, err, tcase.err) 296 } 297 continue 298 } 299 if err != nil { 300 t.Errorf("ValidateBindVars(%v): %v, want nil", tcase.in, err) 301 } 302 } 303 } 304 305 func TestValidateBindVariable(t *testing.T) { 306 testcases := []struct { 307 in *querypb.BindVariable 308 err string 309 }{{ 310 in: &querypb.BindVariable{ 311 Type: querypb.Type_INT8, 312 Value: []byte("1"), 313 }, 314 }, { 315 in: &querypb.BindVariable{ 316 Type: querypb.Type_INT16, 317 Value: []byte("1"), 318 }, 319 }, { 320 in: &querypb.BindVariable{ 321 Type: querypb.Type_INT24, 322 Value: []byte("1"), 323 }, 324 }, { 325 in: &querypb.BindVariable{ 326 Type: querypb.Type_INT32, 327 Value: []byte("1"), 328 }, 329 }, { 330 in: &querypb.BindVariable{ 331 Type: querypb.Type_INT64, 332 Value: []byte("1"), 333 }, 334 }, { 335 in: &querypb.BindVariable{ 336 Type: querypb.Type_UINT8, 337 Value: []byte("1"), 338 }, 339 }, { 340 in: &querypb.BindVariable{ 341 Type: querypb.Type_UINT16, 342 Value: []byte("1"), 343 }, 344 }, { 345 in: &querypb.BindVariable{ 346 Type: querypb.Type_UINT24, 347 Value: []byte("1"), 348 }, 349 }, { 350 in: &querypb.BindVariable{ 351 Type: querypb.Type_UINT32, 352 Value: []byte("1"), 353 }, 354 }, { 355 in: &querypb.BindVariable{ 356 Type: querypb.Type_UINT64, 357 Value: []byte("1"), 358 }, 359 }, { 360 in: &querypb.BindVariable{ 361 Type: querypb.Type_FLOAT32, 362 Value: []byte("1.00"), 363 }, 364 }, { 365 in: &querypb.BindVariable{ 366 Type: querypb.Type_FLOAT64, 367 Value: []byte("1.00"), 368 }, 369 }, { 370 in: &querypb.BindVariable{ 371 Type: querypb.Type_DECIMAL, 372 Value: []byte("1.00"), 373 }, 374 }, { 375 in: &querypb.BindVariable{ 376 Type: querypb.Type_TIMESTAMP, 377 Value: []byte("2012-02-24 23:19:43"), 378 }, 379 }, { 380 in: &querypb.BindVariable{ 381 Type: querypb.Type_DATE, 382 Value: []byte("2012-02-24"), 383 }, 384 }, { 385 in: &querypb.BindVariable{ 386 Type: querypb.Type_TIME, 387 Value: []byte("23:19:43"), 388 }, 389 }, { 390 in: &querypb.BindVariable{ 391 Type: querypb.Type_DATETIME, 392 Value: []byte("2012-02-24 23:19:43"), 393 }, 394 }, { 395 in: &querypb.BindVariable{ 396 Type: querypb.Type_YEAR, 397 Value: []byte("1"), 398 }, 399 }, { 400 in: &querypb.BindVariable{ 401 Type: querypb.Type_TEXT, 402 Value: []byte("a"), 403 }, 404 }, { 405 in: &querypb.BindVariable{ 406 Type: querypb.Type_BLOB, 407 Value: []byte("a"), 408 }, 409 }, { 410 in: &querypb.BindVariable{ 411 Type: querypb.Type_VARCHAR, 412 Value: []byte("a"), 413 }, 414 }, { 415 in: &querypb.BindVariable{ 416 Type: querypb.Type_BINARY, 417 Value: []byte("a"), 418 }, 419 }, { 420 in: &querypb.BindVariable{ 421 Type: querypb.Type_CHAR, 422 Value: []byte("a"), 423 }, 424 }, { 425 in: &querypb.BindVariable{ 426 Type: querypb.Type_BIT, 427 Value: []byte("1"), 428 }, 429 }, { 430 in: &querypb.BindVariable{ 431 Type: querypb.Type_ENUM, 432 Value: []byte("a"), 433 }, 434 }, { 435 in: &querypb.BindVariable{ 436 Type: querypb.Type_SET, 437 Value: []byte("a"), 438 }, 439 }, { 440 in: &querypb.BindVariable{ 441 Type: querypb.Type_VARBINARY, 442 Value: []byte("a"), 443 }, 444 }, { 445 in: &querypb.BindVariable{ 446 Type: querypb.Type_INT64, 447 Value: []byte(InvalidNeg), 448 }, 449 err: "out of range", 450 }, { 451 in: &querypb.BindVariable{ 452 Type: querypb.Type_INT64, 453 Value: []byte(InvalidPos), 454 }, 455 err: "out of range", 456 }, { 457 in: &querypb.BindVariable{ 458 Type: querypb.Type_UINT64, 459 Value: []byte("-1"), 460 }, 461 err: "invalid syntax", 462 }, { 463 in: &querypb.BindVariable{ 464 Type: querypb.Type_UINT64, 465 Value: []byte(InvalidPos), 466 }, 467 err: "out of range", 468 }, { 469 in: &querypb.BindVariable{ 470 Type: querypb.Type_FLOAT64, 471 Value: []byte("a"), 472 }, 473 err: "invalid syntax", 474 }, { 475 in: &querypb.BindVariable{ 476 Type: querypb.Type_EXPRESSION, 477 Value: []byte("a"), 478 }, 479 err: "invalid type specified for MakeValue: EXPRESSION", 480 }, { 481 in: &querypb.BindVariable{ 482 Type: querypb.Type_TUPLE, 483 Values: []*querypb.Value{{ 484 Type: querypb.Type_INT64, 485 Value: []byte("1"), 486 }}, 487 }, 488 }, { 489 in: &querypb.BindVariable{ 490 Type: querypb.Type_TUPLE, 491 }, 492 err: "empty tuple is not allowed", 493 }, { 494 in: &querypb.BindVariable{ 495 Type: querypb.Type_TUPLE, 496 Values: []*querypb.Value{{ 497 Type: querypb.Type_TUPLE, 498 }}, 499 }, 500 err: "tuple not allowed inside another tuple", 501 }} 502 for _, tcase := range testcases { 503 err := ValidateBindVariable(tcase.in) 504 if tcase.err != "" { 505 if err == nil || !strings.Contains(err.Error(), tcase.err) { 506 t.Errorf("ValidateBindVar(%v) error: %v, must contain %v", tcase.in, err, tcase.err) 507 } 508 continue 509 } 510 if err != nil { 511 t.Errorf("ValidateBindVar(%v) error: %v", tcase.in, err) 512 } 513 } 514 515 // Special case: nil bind var. 516 err := ValidateBindVariable(nil) 517 want := "bind variable is nil" 518 if err == nil || err.Error() != want { 519 t.Errorf("ValidateBindVar(nil) error: %v, want %s", err, want) 520 } 521 } 522 523 func TestBindVariableToValue(t *testing.T) { 524 v, err := BindVariableToValue(Int64BindVariable(1)) 525 require.NoError(t, err) 526 assert.Equal(t, MakeTrusted(querypb.Type_INT64, []byte("1")), v) 527 528 _, err = BindVariableToValue(&querypb.BindVariable{Type: querypb.Type_TUPLE}) 529 require.EqualError(t, err, "cannot convert a TUPLE bind var into a value") 530 531 v, err = BindVariableToValue(BitNumBindVariable([]byte("0b101"))) 532 require.NoError(t, err) 533 assert.Equal(t, MakeTrusted(querypb.Type_BITNUM, []byte("0b101")), v) 534 535 } 536 537 func TestBindVariablesEqual(t *testing.T) { 538 bv1 := map[string]*querypb.BindVariable{ 539 "k": { 540 Type: querypb.Type_INT64, 541 Value: []byte("1"), 542 }, 543 } 544 bv2 := map[string]*querypb.BindVariable{ 545 "k": { 546 Type: querypb.Type_INT64, 547 Value: []byte("1"), 548 }, 549 } 550 bv3 := map[string]*querypb.BindVariable{ 551 "k": { 552 Type: querypb.Type_INT64, 553 Value: []byte("1"), 554 }, 555 } 556 if !BindVariablesEqual(bv1, bv2) { 557 t.Errorf("%v != %v, want equal", bv1, bv2) 558 } 559 if !BindVariablesEqual(bv1, bv3) { 560 t.Errorf("%v = %v, want not equal", bv1, bv3) 561 } 562 } 563 564 func TestBindVariablesFormat(t *testing.T) { 565 tupleBindVar, err := BuildBindVariable([]int64{1, 2}) 566 if err != nil { 567 t.Fatalf("failed to create a tuple bind var: %v", err) 568 } 569 570 bindVariables := map[string]*querypb.BindVariable{ 571 "key_1": StringBindVariable("val_1"), 572 "key_2": Int64BindVariable(789), 573 "key_3": BytesBindVariable([]byte("val_3")), 574 "key_4": tupleBindVar, 575 } 576 577 formattedStr := FormatBindVariables(bindVariables, true /* full */, false /* asJSON */) 578 if !strings.Contains(formattedStr, "key_1") || 579 !strings.Contains(formattedStr, "val_1") { 580 t.Fatalf("bind variable 'key_1': 'val_1' is not formatted") 581 } 582 if !strings.Contains(formattedStr, "key_2") || 583 !strings.Contains(formattedStr, "789") { 584 t.Fatalf("bind variable 'key_2': '789' is not formatted") 585 } 586 if !strings.Contains(formattedStr, "key_3") || !strings.Contains(formattedStr, "val_3") { 587 t.Fatalf("bind variable 'key_3': 'val_3' is not formatted") 588 } 589 if !strings.Contains(formattedStr, "key_4:type:TUPLE") { 590 t.Fatalf("bind variable 'key_4': (1, 2) is not formatted") 591 } 592 593 formattedStr = FormatBindVariables(bindVariables, false /* full */, false /* asJSON */) 594 if !strings.Contains(formattedStr, "key_1") { 595 t.Fatalf("bind variable 'key_1' is not formatted") 596 } 597 if !strings.Contains(formattedStr, "key_2") || 598 !strings.Contains(formattedStr, "789") { 599 t.Fatalf("bind variable 'key_2': '789' is not formatted") 600 } 601 if !strings.Contains(formattedStr, "key_3") || !strings.Contains(formattedStr, "5 bytes") { 602 t.Fatalf("bind variable 'key_3' is not formatted") 603 } 604 if !strings.Contains(formattedStr, "key_4") || !strings.Contains(formattedStr, "2 items") { 605 t.Fatalf("bind variable 'key_4' is not formatted") 606 } 607 608 formattedStr = FormatBindVariables(bindVariables, true /* full */, true /* asJSON */) 609 t.Logf("%q", formattedStr) 610 if !strings.Contains(formattedStr, "\"key_1\": {\"type\": \"VARCHAR\", \"value\": \"val_1\"}") { 611 t.Fatalf("bind variable 'key_1' is not formatted") 612 } 613 614 if !strings.Contains(formattedStr, "\"key_2\": {\"type\": \"INT64\", \"value\": 789}") { 615 t.Fatalf("bind variable 'key_2' is not formatted") 616 } 617 618 if !strings.Contains(formattedStr, "\"key_3\": {\"type\": \"VARBINARY\", \"value\": \"val_3\"}") { 619 t.Fatalf("bind variable 'key_3' is not formatted") 620 } 621 622 if !strings.Contains(formattedStr, "\"key_4\": {\"type\": \"TUPLE\", \"value\": \"\"}") { 623 t.Fatalf("bind variable 'key_4' is not formatted") 624 } 625 626 formattedStr = FormatBindVariables(bindVariables, false /* full */, true /* asJSON */) 627 if !strings.Contains(formattedStr, "\"key_1\": {\"type\": \"VARCHAR\", \"value\": \"5 bytes\"}") { 628 t.Fatalf("bind variable 'key_1' is not formatted") 629 } 630 631 if !strings.Contains(formattedStr, "\"key_2\": {\"type\": \"INT64\", \"value\": 789}") { 632 t.Fatalf("bind variable 'key_2' is not formatted") 633 } 634 635 if !strings.Contains(formattedStr, "\"key_3\": {\"type\": \"VARCHAR\", \"value\": \"5 bytes\"}") { 636 t.Fatalf("bind variable 'key_3' is not formatted") 637 } 638 639 if !strings.Contains(formattedStr, "\"key_4\": {\"type\": \"VARCHAR\", \"value\": \"2 items\"}") { 640 t.Fatalf("bind variable 'key_4' is not formatted") 641 } 642 }