vitess.io/vitess@v0.16.2/go/vt/vtgate/evalengine/arithmetic_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 evalengine 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 "math" 23 "reflect" 24 "strconv" 25 "testing" 26 27 "vitess.io/vitess/go/mysql/collations" 28 "vitess.io/vitess/go/test/utils" 29 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 33 "vitess.io/vitess/go/sqltypes" 34 35 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 36 "vitess.io/vitess/go/vt/vterrors" 37 ) 38 39 var ( 40 NULL = sqltypes.NULL 41 NewInt32 = sqltypes.NewInt32 42 NewInt64 = sqltypes.NewInt64 43 NewUint64 = sqltypes.NewUint64 44 NewFloat64 = sqltypes.NewFloat64 45 TestValue = sqltypes.TestValue 46 NewDecimal = sqltypes.NewDecimal 47 48 maxUint64 uint64 = math.MaxUint64 49 ) 50 51 func TestArithmetics(t *testing.T) { 52 type tcase struct { 53 v1, v2, out sqltypes.Value 54 err string 55 } 56 57 tests := []struct { 58 operator string 59 f func(a, b sqltypes.Value) (sqltypes.Value, error) 60 cases []tcase 61 }{{ 62 operator: "-", 63 f: Subtract, 64 cases: []tcase{{ 65 // All Nulls 66 v1: NULL, 67 v2: NULL, 68 out: NULL, 69 }, { 70 // First value null. 71 v1: NewInt32(1), 72 v2: NULL, 73 out: NULL, 74 }, { 75 // Second value null. 76 v1: NULL, 77 v2: NewInt32(1), 78 out: NULL, 79 }, { 80 // case with negative value 81 v1: NewInt64(-1), 82 v2: NewInt64(-2), 83 out: NewInt64(1), 84 }, { 85 // testing for int64 overflow with min negative value 86 v1: NewInt64(math.MinInt64), 87 v2: NewInt64(1), 88 err: dataOutOfRangeError(math.MinInt64, 1, "BIGINT", "-").Error(), 89 }, { 90 v1: NewUint64(4), 91 v2: NewInt64(5), 92 err: dataOutOfRangeError(4, 5, "BIGINT UNSIGNED", "-").Error(), 93 }, { 94 // testing uint - int 95 v1: NewUint64(7), 96 v2: NewInt64(5), 97 out: NewUint64(2), 98 }, { 99 v1: NewUint64(math.MaxUint64), 100 v2: NewInt64(0), 101 out: NewUint64(math.MaxUint64), 102 }, { 103 // testing for int64 overflow 104 v1: NewInt64(math.MinInt64), 105 v2: NewUint64(0), 106 err: dataOutOfRangeError(math.MinInt64, 0, "BIGINT UNSIGNED", "-").Error(), 107 }, { 108 v1: TestValue(sqltypes.VarChar, "c"), 109 v2: NewInt64(1), 110 out: NewFloat64(-1), 111 }, { 112 v1: NewUint64(1), 113 v2: TestValue(sqltypes.VarChar, "c"), 114 out: NewFloat64(1), 115 }, { 116 // testing for error for parsing float value to uint64 117 v1: TestValue(sqltypes.Uint64, "1.2"), 118 v2: NewInt64(2), 119 err: "strconv.ParseUint: parsing \"1.2\": invalid syntax", 120 }, { 121 // testing for error for parsing float value to uint64 122 v1: NewUint64(2), 123 v2: TestValue(sqltypes.Uint64, "1.2"), 124 err: "strconv.ParseUint: parsing \"1.2\": invalid syntax", 125 }, { 126 // uint64 - uint64 127 v1: NewUint64(8), 128 v2: NewUint64(4), 129 out: NewUint64(4), 130 }, { 131 // testing for float subtraction: float - int 132 v1: NewFloat64(1.2), 133 v2: NewInt64(2), 134 out: NewFloat64(-0.8), 135 }, { 136 // testing for float subtraction: float - uint 137 v1: NewFloat64(1.2), 138 v2: NewUint64(2), 139 out: NewFloat64(-0.8), 140 }, { 141 v1: NewInt64(-1), 142 v2: NewUint64(2), 143 err: dataOutOfRangeError(-1, 2, "BIGINT UNSIGNED", "-").Error(), 144 }, { 145 v1: NewInt64(2), 146 v2: NewUint64(1), 147 out: NewUint64(1), 148 }, { 149 // testing int64 - float64 method 150 v1: NewInt64(-2), 151 v2: NewFloat64(1.0), 152 out: NewFloat64(-3.0), 153 }, { 154 // testing uint64 - float64 method 155 v1: NewUint64(1), 156 v2: NewFloat64(-2.0), 157 out: NewFloat64(3.0), 158 }, { 159 // testing uint - int to return uintplusint 160 v1: NewUint64(1), 161 v2: NewInt64(-2), 162 out: NewUint64(3), 163 }, { 164 // testing for float - float 165 v1: NewFloat64(1.2), 166 v2: NewFloat64(3.2), 167 out: NewFloat64(-2), 168 }, { 169 // testing uint - uint if v2 > v1 170 v1: NewUint64(2), 171 v2: NewUint64(4), 172 err: dataOutOfRangeError(2, 4, "BIGINT UNSIGNED", "-").Error(), 173 }, { 174 // testing uint - (- int) 175 v1: NewUint64(1), 176 v2: NewInt64(-2), 177 out: NewUint64(3), 178 }}, 179 }, { 180 operator: "+", 181 f: Add, 182 cases: []tcase{{ 183 // All Nulls 184 v1: NULL, 185 v2: NULL, 186 out: NULL, 187 }, { 188 // First value null. 189 v1: NewInt32(1), 190 v2: NULL, 191 out: NULL, 192 }, { 193 // Second value null. 194 v1: NULL, 195 v2: NewInt32(1), 196 out: NULL, 197 }, { 198 // case with negatives 199 v1: NewInt64(-1), 200 v2: NewInt64(-2), 201 out: NewInt64(-3), 202 }, { 203 // testing for overflow int64, result will be unsigned int 204 v1: NewInt64(math.MaxInt64), 205 v2: NewUint64(2), 206 out: NewUint64(9223372036854775809), 207 }, { 208 v1: NewInt64(-2), 209 v2: NewUint64(1), 210 err: dataOutOfRangeError(1, -2, "BIGINT UNSIGNED", "+").Error(), 211 }, { 212 v1: NewInt64(math.MaxInt64), 213 v2: NewInt64(-2), 214 out: NewInt64(9223372036854775805), 215 }, { 216 // Normal case 217 v1: NewUint64(1), 218 v2: NewUint64(2), 219 out: NewUint64(3), 220 }, { 221 // testing for overflow uint64 222 v1: NewUint64(maxUint64), 223 v2: NewUint64(2), 224 err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(), 225 }, { 226 // int64 underflow 227 v1: NewInt64(math.MinInt64), 228 v2: NewInt64(-2), 229 err: dataOutOfRangeError(math.MinInt64, -2, "BIGINT", "+").Error(), 230 }, { 231 // checking int64 max value can be returned 232 v1: NewInt64(math.MaxInt64), 233 v2: NewUint64(0), 234 out: NewUint64(9223372036854775807), 235 }, { 236 // testing whether uint64 max value can be returned 237 v1: NewUint64(math.MaxUint64), 238 v2: NewInt64(0), 239 out: NewUint64(math.MaxUint64), 240 }, { 241 v1: NewUint64(math.MaxInt64), 242 v2: NewInt64(1), 243 out: NewUint64(9223372036854775808), 244 }, { 245 v1: NewUint64(1), 246 v2: TestValue(sqltypes.VarChar, "c"), 247 out: NewFloat64(1), 248 }, { 249 v1: NewUint64(1), 250 v2: TestValue(sqltypes.VarChar, "1.2"), 251 out: NewFloat64(2.2), 252 }, { 253 v1: TestValue(sqltypes.Int64, "1.2"), 254 v2: NewInt64(2), 255 err: "strconv.ParseInt: parsing \"1.2\": invalid syntax", 256 }, { 257 v1: NewInt64(2), 258 v2: TestValue(sqltypes.Int64, "1.2"), 259 err: "strconv.ParseInt: parsing \"1.2\": invalid syntax", 260 }, { 261 // testing for uint64 overflow with max uint64 + int value 262 v1: NewUint64(maxUint64), 263 v2: NewInt64(2), 264 err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(), 265 }, { 266 v1: sqltypes.NewHexNum([]byte("0x9")), 267 v2: NewInt64(1), 268 out: NewUint64(10), 269 }}, 270 }, { 271 operator: "/", 272 f: Divide, 273 cases: []tcase{{ 274 //All Nulls 275 v1: NULL, 276 v2: NULL, 277 out: NULL, 278 }, { 279 // First value null. 280 v1: NULL, 281 v2: NewInt32(1), 282 out: NULL, 283 }, { 284 // Second value null. 285 v1: NewInt32(1), 286 v2: NULL, 287 out: NULL, 288 }, { 289 // Second arg 0 290 v1: NewInt32(5), 291 v2: NewInt32(0), 292 out: NULL, 293 }, { 294 // Both arguments zero 295 v1: NewInt32(0), 296 v2: NewInt32(0), 297 out: NULL, 298 }, { 299 // case with negative value 300 v1: NewInt64(-1), 301 v2: NewInt64(-2), 302 out: NewDecimal("0.5000"), 303 }, { 304 // float64 division by zero 305 v1: NewFloat64(2), 306 v2: NewFloat64(0), 307 out: NULL, 308 }, { 309 // Lower bound for int64 310 v1: NewInt64(math.MinInt64), 311 v2: NewInt64(1), 312 out: NewDecimal(strconv.Itoa(math.MinInt64) + ".0000"), 313 }, { 314 // upper bound for uint64 315 v1: NewUint64(math.MaxUint64), 316 v2: NewUint64(1), 317 out: NewDecimal(strconv.FormatUint(math.MaxUint64, 10) + ".0000"), 318 }, { 319 // testing for error in types 320 v1: TestValue(sqltypes.Int64, "1.2"), 321 v2: NewInt64(2), 322 err: "strconv.ParseInt: parsing \"1.2\": invalid syntax", 323 }, { 324 // testing for error in types 325 v1: NewInt64(2), 326 v2: TestValue(sqltypes.Int64, "1.2"), 327 err: "strconv.ParseInt: parsing \"1.2\": invalid syntax", 328 }, { 329 // testing for uint/int 330 v1: NewUint64(4), 331 v2: NewInt64(5), 332 out: NewDecimal("0.8000"), 333 }, { 334 // testing for uint/uint 335 v1: NewUint64(1), 336 v2: NewUint64(2), 337 out: NewDecimal("0.5000"), 338 }, { 339 // testing for float64/int64 340 v1: TestValue(sqltypes.Float64, "1.2"), 341 v2: NewInt64(-2), 342 out: NewFloat64(-0.6), 343 }, { 344 // testing for float64/uint64 345 v1: TestValue(sqltypes.Float64, "1.2"), 346 v2: NewUint64(2), 347 out: NewFloat64(0.6), 348 }, { 349 // testing for overflow of float64 350 v1: NewFloat64(math.MaxFloat64), 351 v2: NewFloat64(0.5), 352 err: dataOutOfRangeError(math.MaxFloat64, 0.5, "BIGINT", "/").Error(), 353 }}, 354 }, { 355 operator: "*", 356 f: Multiply, 357 cases: []tcase{{ 358 //All Nulls 359 v1: NULL, 360 v2: NULL, 361 out: NULL, 362 }, { 363 // First value null. 364 v1: NewInt32(1), 365 v2: NULL, 366 out: NULL, 367 }, { 368 // Second value null. 369 v1: NULL, 370 v2: NewInt32(1), 371 out: NULL, 372 }, { 373 // case with negative value 374 v1: NewInt64(-1), 375 v2: NewInt64(-2), 376 out: NewInt64(2), 377 }, { 378 // testing for int64 overflow with min negative value 379 v1: NewInt64(math.MinInt64), 380 v2: NewInt64(1), 381 out: NewInt64(math.MinInt64), 382 }, { 383 // testing for error in types 384 v1: TestValue(sqltypes.Int64, "1.2"), 385 v2: NewInt64(2), 386 err: "strconv.ParseInt: parsing \"1.2\": invalid syntax", 387 }, { 388 // testing for error in types 389 v1: NewInt64(2), 390 v2: TestValue(sqltypes.Int64, "1.2"), 391 err: "strconv.ParseInt: parsing \"1.2\": invalid syntax", 392 }, { 393 // testing for uint*int 394 v1: NewUint64(4), 395 v2: NewInt64(5), 396 out: NewUint64(20), 397 }, { 398 // testing for uint*uint 399 v1: NewUint64(1), 400 v2: NewUint64(2), 401 out: NewUint64(2), 402 }, { 403 // testing for float64*int64 404 v1: TestValue(sqltypes.Float64, "1.2"), 405 v2: NewInt64(-2), 406 out: NewFloat64(-2.4), 407 }, { 408 // testing for float64*uint64 409 v1: TestValue(sqltypes.Float64, "1.2"), 410 v2: NewUint64(2), 411 out: NewFloat64(2.4), 412 }, { 413 // testing for overflow of int64 414 v1: NewInt64(math.MaxInt64), 415 v2: NewInt64(2), 416 err: dataOutOfRangeError(math.MaxInt64, 2, "BIGINT", "*").Error(), 417 }, { 418 // testing for underflow of uint64*max.uint64 419 v1: NewInt64(2), 420 v2: NewUint64(maxUint64), 421 err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "*").Error(), 422 }, { 423 v1: NewUint64(math.MaxUint64), 424 v2: NewUint64(1), 425 out: NewUint64(math.MaxUint64), 426 }, { 427 //Checking whether maxInt value can be passed as uint value 428 v1: NewUint64(math.MaxInt64), 429 v2: NewInt64(3), 430 err: dataOutOfRangeError(math.MaxInt64, 3, "BIGINT UNSIGNED", "*").Error(), 431 }}, 432 }} 433 434 for _, test := range tests { 435 t.Run(test.operator, func(t *testing.T) { 436 for _, tcase := range test.cases { 437 name := fmt.Sprintf("%s%s%s", tcase.v1.String(), test.operator, tcase.v2.String()) 438 t.Run(name, func(t *testing.T) { 439 got, err := test.f(tcase.v1, tcase.v2) 440 if tcase.err == "" { 441 require.NoError(t, err) 442 require.Equal(t, tcase.out, got) 443 } else { 444 require.EqualError(t, err, tcase.err) 445 } 446 }) 447 } 448 }) 449 } 450 } 451 452 func TestNullSafeAdd(t *testing.T) { 453 tcases := []struct { 454 v1, v2 sqltypes.Value 455 out sqltypes.Value 456 err error 457 }{{ 458 // All nulls. 459 v1: NULL, 460 v2: NULL, 461 out: NewInt64(0), 462 }, { 463 // First value null. 464 v1: NewInt32(1), 465 v2: NULL, 466 out: NewInt64(1), 467 }, { 468 // Second value null. 469 v1: NULL, 470 v2: NewInt32(1), 471 out: NewInt64(1), 472 }, { 473 // Normal case. 474 v1: NewInt64(1), 475 v2: NewInt64(2), 476 out: NewInt64(3), 477 }, { 478 // Make sure underlying error is returned for LHS. 479 v1: TestValue(sqltypes.Int64, "1.2"), 480 v2: NewInt64(2), 481 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"), 482 }, { 483 // Make sure underlying error is returned for RHS. 484 v1: NewInt64(2), 485 v2: TestValue(sqltypes.Int64, "1.2"), 486 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"), 487 }, { 488 // Make sure underlying error is returned while adding. 489 v1: NewInt64(-1), 490 v2: NewUint64(2), 491 out: NewInt64(1), 492 }, { 493 v1: NewInt64(-100), 494 v2: NewUint64(10), 495 err: dataOutOfRangeError(10, -100, "BIGINT UNSIGNED", "+"), 496 }, { 497 // Make sure underlying error is returned while converting. 498 v1: NewFloat64(1), 499 v2: NewFloat64(2), 500 out: NewInt64(3), 501 }} 502 for _, tcase := range tcases { 503 got, err := NullSafeAdd(tcase.v1, tcase.v2, sqltypes.Int64) 504 505 if tcase.err == nil { 506 require.NoError(t, err) 507 } else { 508 require.EqualError(t, err, tcase.err.Error()) 509 } 510 511 if !reflect.DeepEqual(got, tcase.out) { 512 t.Errorf("NullSafeAdd(%v, %v): %v, want %v", printValue(tcase.v1), printValue(tcase.v2), printValue(got), printValue(tcase.out)) 513 } 514 } 515 } 516 517 func TestCast(t *testing.T) { 518 tcases := []struct { 519 typ sqltypes.Type 520 v sqltypes.Value 521 out sqltypes.Value 522 err error 523 }{{ 524 typ: sqltypes.VarChar, 525 v: NULL, 526 out: NULL, 527 }, { 528 typ: sqltypes.VarChar, 529 v: TestValue(sqltypes.VarChar, "exact types"), 530 out: TestValue(sqltypes.VarChar, "exact types"), 531 }, { 532 typ: sqltypes.Int64, 533 v: TestValue(sqltypes.Int32, "32"), 534 out: TestValue(sqltypes.Int64, "32"), 535 }, { 536 typ: sqltypes.Int24, 537 v: TestValue(sqltypes.Uint64, "64"), 538 out: TestValue(sqltypes.Int24, "64"), 539 }, { 540 typ: sqltypes.Int24, 541 v: TestValue(sqltypes.VarChar, "bad int"), 542 err: vterrors.New(vtrpcpb.Code_UNKNOWN, `strconv.ParseInt: parsing "bad int": invalid syntax`), 543 }, { 544 typ: sqltypes.Uint64, 545 v: TestValue(sqltypes.Uint32, "32"), 546 out: TestValue(sqltypes.Uint64, "32"), 547 }, { 548 typ: sqltypes.Uint24, 549 v: TestValue(sqltypes.Int64, "64"), 550 out: TestValue(sqltypes.Uint24, "64"), 551 }, { 552 typ: sqltypes.Uint24, 553 v: TestValue(sqltypes.Int64, "-1"), 554 err: vterrors.New(vtrpcpb.Code_UNKNOWN, `strconv.ParseUint: parsing "-1": invalid syntax`), 555 }, { 556 typ: sqltypes.Float64, 557 v: TestValue(sqltypes.Int64, "64"), 558 out: TestValue(sqltypes.Float64, "64"), 559 }, { 560 typ: sqltypes.Float32, 561 v: TestValue(sqltypes.Float64, "64"), 562 out: TestValue(sqltypes.Float32, "64"), 563 }, { 564 typ: sqltypes.Float32, 565 v: TestValue(sqltypes.Decimal, "1.24"), 566 out: TestValue(sqltypes.Float32, "1.24"), 567 }, { 568 typ: sqltypes.Float64, 569 v: TestValue(sqltypes.VarChar, "1.25"), 570 out: TestValue(sqltypes.Float64, "1.25"), 571 }, { 572 typ: sqltypes.Float64, 573 v: TestValue(sqltypes.VarChar, "bad float"), 574 err: vterrors.New(vtrpcpb.Code_UNKNOWN, `strconv.ParseFloat: parsing "bad float": invalid syntax`), 575 }, { 576 typ: sqltypes.VarChar, 577 v: TestValue(sqltypes.Int64, "64"), 578 out: TestValue(sqltypes.VarChar, "64"), 579 }, { 580 typ: sqltypes.VarBinary, 581 v: TestValue(sqltypes.Float64, "64"), 582 out: TestValue(sqltypes.VarBinary, "64"), 583 }, { 584 typ: sqltypes.VarBinary, 585 v: TestValue(sqltypes.Decimal, "1.24"), 586 out: TestValue(sqltypes.VarBinary, "1.24"), 587 }, { 588 typ: sqltypes.VarBinary, 589 v: TestValue(sqltypes.VarChar, "1.25"), 590 out: TestValue(sqltypes.VarBinary, "1.25"), 591 }, { 592 typ: sqltypes.VarChar, 593 v: TestValue(sqltypes.VarBinary, "valid string"), 594 out: TestValue(sqltypes.VarChar, "valid string"), 595 }, { 596 typ: sqltypes.VarChar, 597 v: TestValue(sqltypes.Expression, "bad string"), 598 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "expression cannot be converted to bytes"), 599 }} 600 for _, tcase := range tcases { 601 got, err := Cast(tcase.v, tcase.typ) 602 if !vterrors.Equals(err, tcase.err) { 603 t.Errorf("Cast(%v) error: %v, want %v", tcase.v, vterrors.Print(err), vterrors.Print(tcase.err)) 604 } 605 if tcase.err != nil { 606 continue 607 } 608 609 if !reflect.DeepEqual(got, tcase.out) { 610 t.Errorf("Cast(%v): %v, want %v", tcase.v, got, tcase.out) 611 } 612 } 613 } 614 615 func TestToUint64(t *testing.T) { 616 tcases := []struct { 617 v sqltypes.Value 618 out uint64 619 err error 620 }{{ 621 v: TestValue(sqltypes.VarChar, "abcd"), 622 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "could not parse value: 'abcd'"), 623 }, { 624 v: NewInt64(-1), 625 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "negative number cannot be converted to unsigned: -1"), 626 }, { 627 v: NewInt64(1), 628 out: 1, 629 }, { 630 v: NewUint64(1), 631 out: 1, 632 }} 633 for _, tcase := range tcases { 634 got, err := ToUint64(tcase.v) 635 if !vterrors.Equals(err, tcase.err) { 636 t.Errorf("ToUint64(%v) error: %v, want %v", tcase.v, vterrors.Print(err), vterrors.Print(tcase.err)) 637 } 638 if tcase.err != nil { 639 continue 640 } 641 642 if got != tcase.out { 643 t.Errorf("ToUint64(%v): %v, want %v", tcase.v, got, tcase.out) 644 } 645 } 646 } 647 648 func TestToInt64(t *testing.T) { 649 tcases := []struct { 650 v sqltypes.Value 651 out int64 652 err error 653 }{{ 654 v: TestValue(sqltypes.VarChar, "abcd"), 655 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "could not parse value: 'abcd'"), 656 }, { 657 v: NewUint64(18446744073709551615), 658 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "unsigned number overflows int64 value: 18446744073709551615"), 659 }, { 660 v: NewInt64(1), 661 out: 1, 662 }, { 663 v: NewUint64(1), 664 out: 1, 665 }} 666 for _, tcase := range tcases { 667 got, err := ToInt64(tcase.v) 668 if !vterrors.Equals(err, tcase.err) { 669 t.Errorf("ToInt64(%v) error: %v, want %v", tcase.v, vterrors.Print(err), vterrors.Print(tcase.err)) 670 } 671 if tcase.err != nil { 672 continue 673 } 674 675 if got != tcase.out { 676 t.Errorf("ToInt64(%v): %v, want %v", tcase.v, got, tcase.out) 677 } 678 } 679 } 680 681 func TestToFloat64(t *testing.T) { 682 tcases := []struct { 683 v sqltypes.Value 684 out float64 685 err error 686 }{{ 687 v: TestValue(sqltypes.VarChar, "abcd"), 688 out: 0, 689 }, { 690 v: TestValue(sqltypes.VarChar, "1.2"), 691 out: 1.2, 692 }, { 693 v: NewInt64(1), 694 out: 1, 695 }, { 696 v: NewUint64(1), 697 out: 1, 698 }, { 699 v: NewFloat64(1.2), 700 out: 1.2, 701 }, { 702 v: TestValue(sqltypes.Int64, "1.2"), 703 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"), 704 }} 705 for _, tcase := range tcases { 706 t.Run(tcase.v.String(), func(t *testing.T) { 707 got, err := ToFloat64(tcase.v) 708 if tcase.err != nil { 709 require.EqualError(t, err, tcase.err.Error()) 710 } else { 711 require.Equal(t, tcase.out, got) 712 } 713 }) 714 } 715 } 716 717 func TestToNative(t *testing.T) { 718 testcases := []struct { 719 in sqltypes.Value 720 out any 721 }{{ 722 in: NULL, 723 out: nil, 724 }, { 725 in: TestValue(sqltypes.Int8, "1"), 726 out: int64(1), 727 }, { 728 in: TestValue(sqltypes.Int16, "1"), 729 out: int64(1), 730 }, { 731 in: TestValue(sqltypes.Int24, "1"), 732 out: int64(1), 733 }, { 734 in: TestValue(sqltypes.Int32, "1"), 735 out: int64(1), 736 }, { 737 in: TestValue(sqltypes.Int64, "1"), 738 out: int64(1), 739 }, { 740 in: TestValue(sqltypes.Uint8, "1"), 741 out: uint64(1), 742 }, { 743 in: TestValue(sqltypes.Uint16, "1"), 744 out: uint64(1), 745 }, { 746 in: TestValue(sqltypes.Uint24, "1"), 747 out: uint64(1), 748 }, { 749 in: TestValue(sqltypes.Uint32, "1"), 750 out: uint64(1), 751 }, { 752 in: TestValue(sqltypes.Uint64, "1"), 753 out: uint64(1), 754 }, { 755 in: TestValue(sqltypes.Float32, "1"), 756 out: float64(1), 757 }, { 758 in: TestValue(sqltypes.Float64, "1"), 759 out: float64(1), 760 }, { 761 in: TestValue(sqltypes.Timestamp, "2012-02-24 23:19:43"), 762 out: []byte("2012-02-24 23:19:43"), 763 }, { 764 in: TestValue(sqltypes.Date, "2012-02-24"), 765 out: []byte("2012-02-24"), 766 }, { 767 in: TestValue(sqltypes.Time, "23:19:43"), 768 out: []byte("23:19:43"), 769 }, { 770 in: TestValue(sqltypes.Datetime, "2012-02-24 23:19:43"), 771 out: []byte("2012-02-24 23:19:43"), 772 }, { 773 in: TestValue(sqltypes.Year, "1"), 774 out: uint64(1), 775 }, { 776 in: TestValue(sqltypes.Decimal, "1"), 777 out: []byte("1"), 778 }, { 779 in: TestValue(sqltypes.Text, "a"), 780 out: []byte("a"), 781 }, { 782 in: TestValue(sqltypes.Blob, "a"), 783 out: []byte("a"), 784 }, { 785 in: TestValue(sqltypes.VarChar, "a"), 786 out: []byte("a"), 787 }, { 788 in: TestValue(sqltypes.VarBinary, "a"), 789 out: []byte("a"), 790 }, { 791 in: TestValue(sqltypes.Char, "a"), 792 out: []byte("a"), 793 }, { 794 in: TestValue(sqltypes.Binary, "a"), 795 out: []byte("a"), 796 }, { 797 in: TestValue(sqltypes.Bit, "1"), 798 out: []byte("1"), 799 }, { 800 in: TestValue(sqltypes.Enum, "a"), 801 out: []byte("a"), 802 }, { 803 in: TestValue(sqltypes.Set, "a"), 804 out: []byte("a"), 805 }} 806 for _, tcase := range testcases { 807 v, err := ToNative(tcase.in) 808 if err != nil { 809 t.Error(err) 810 } 811 if !reflect.DeepEqual(v, tcase.out) { 812 t.Errorf("%v.ToNative = %#v, want %#v", tcase.in, v, tcase.out) 813 } 814 } 815 816 // Test Expression failure. 817 _, err := ToNative(TestValue(sqltypes.Expression, "aa")) 818 want := vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "EXPRESSION(aa) cannot be converted to a go type") 819 if !vterrors.Equals(err, want) { 820 t.Errorf("ToNative(EXPRESSION): %v, want %v", vterrors.Print(err), vterrors.Print(want)) 821 } 822 } 823 824 func TestNewIntegralNumeric(t *testing.T) { 825 tcases := []struct { 826 v sqltypes.Value 827 out EvalResult 828 err error 829 }{{ 830 v: NewInt64(1), 831 out: newEvalInt64(1), 832 }, { 833 v: NewUint64(1), 834 out: newEvalUint64(1), 835 }, { 836 v: NewFloat64(1), 837 out: newEvalInt64(1), 838 }, { 839 // For non-number type, Int64 is the default. 840 v: TestValue(sqltypes.VarChar, "1"), 841 out: newEvalInt64(1), 842 }, { 843 // If Int64 can't work, we use Uint64. 844 v: TestValue(sqltypes.VarChar, "18446744073709551615"), 845 out: newEvalUint64(18446744073709551615), 846 }, { 847 // Only valid Int64 allowed if type is Int64. 848 v: TestValue(sqltypes.Int64, "1.2"), 849 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"), 850 }, { 851 // Only valid Uint64 allowed if type is Uint64. 852 v: TestValue(sqltypes.Uint64, "1.2"), 853 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseUint: parsing \"1.2\": invalid syntax"), 854 }, { 855 v: TestValue(sqltypes.VarChar, "abcd"), 856 err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "could not parse value: 'abcd'"), 857 }} 858 for _, tcase := range tcases { 859 got, err := newEvalResultNumeric(tcase.v) 860 if err != nil && !vterrors.Equals(err, tcase.err) { 861 t.Errorf("newIntegralNumeric(%s) error: %v, want %v", printValue(tcase.v), vterrors.Print(err), vterrors.Print(tcase.err)) 862 } 863 if tcase.err == nil { 864 continue 865 } 866 867 utils.MustMatch(t, tcase.out, got, "newIntegralNumeric") 868 } 869 } 870 871 func TestAddNumeric(t *testing.T) { 872 tcases := []struct { 873 v1, v2 EvalResult 874 out EvalResult 875 err error 876 }{{ 877 v1: newEvalInt64(1), 878 v2: newEvalInt64(2), 879 out: newEvalInt64(3), 880 }, { 881 v1: newEvalInt64(1), 882 v2: newEvalUint64(2), 883 out: newEvalUint64(3), 884 }, { 885 v1: newEvalInt64(1), 886 v2: newEvalFloat(2), 887 out: newEvalFloat(3), 888 }, { 889 v1: newEvalUint64(1), 890 v2: newEvalUint64(2), 891 out: newEvalUint64(3), 892 }, { 893 v1: newEvalUint64(1), 894 v2: newEvalFloat(2), 895 out: newEvalFloat(3), 896 }, { 897 v1: newEvalFloat(1), 898 v2: newEvalFloat(2), 899 out: newEvalFloat(3), 900 }, { 901 // Int64 overflow. 902 v1: newEvalInt64(9223372036854775807), 903 v2: newEvalInt64(2), 904 err: dataOutOfRangeError(9223372036854775807, 2, "BIGINT", "+"), 905 }, { 906 // Int64 underflow. 907 v1: newEvalInt64(-9223372036854775807), 908 v2: newEvalInt64(-2), 909 err: dataOutOfRangeError(-9223372036854775807, -2, "BIGINT", "+"), 910 }, { 911 v1: newEvalInt64(-1), 912 v2: newEvalUint64(2), 913 out: newEvalUint64(1), 914 }, { 915 // Uint64 overflow. 916 v1: newEvalUint64(18446744073709551615), 917 v2: newEvalUint64(2), 918 err: dataOutOfRangeError(uint64(18446744073709551615), 2, "BIGINT UNSIGNED", "+"), 919 }} 920 for _, tcase := range tcases { 921 var got EvalResult 922 err := addNumericWithError(&tcase.v1, &tcase.v2, &got) 923 if err != nil { 924 if tcase.err == nil { 925 t.Fatal(err) 926 } 927 if err.Error() != tcase.err.Error() { 928 t.Fatalf("bad error message: got %q want %q", err, tcase.err) 929 } 930 continue 931 } 932 utils.MustMatch(t, tcase.out, got, "addNumeric") 933 } 934 } 935 936 func TestPrioritize(t *testing.T) { 937 ival := newEvalInt64(-1) 938 uval := newEvalUint64(1) 939 fval := newEvalFloat(1.2) 940 textIntval := newEvalRaw(sqltypes.VarBinary, []byte("-1")) 941 textFloatval := newEvalRaw(sqltypes.VarBinary, []byte("1.2")) 942 943 tcases := []struct { 944 v1, v2 EvalResult 945 out1, out2 EvalResult 946 }{{ 947 v1: ival, 948 v2: uval, 949 out1: uval, 950 out2: ival, 951 }, { 952 v1: ival, 953 v2: fval, 954 out1: fval, 955 out2: ival, 956 }, { 957 v1: uval, 958 v2: ival, 959 out1: uval, 960 out2: ival, 961 }, { 962 v1: uval, 963 v2: fval, 964 out1: fval, 965 out2: uval, 966 }, { 967 v1: fval, 968 v2: ival, 969 out1: fval, 970 out2: ival, 971 }, { 972 v1: fval, 973 v2: uval, 974 out1: fval, 975 out2: uval, 976 }, { 977 v1: textIntval, 978 v2: ival, 979 out1: newEvalFloat(-1.0), 980 out2: ival, 981 }, { 982 v1: ival, 983 v2: textFloatval, 984 out1: fval, 985 out2: ival, 986 }} 987 for _, tcase := range tcases { 988 t.Run(tcase.v1.Value().String()+" - "+tcase.v2.Value().String(), func(t *testing.T) { 989 got1, got2 := makeNumericAndPrioritize(&tcase.v1, &tcase.v2) 990 utils.MustMatch(t, tcase.out1.value(), got1.value(), "makeNumericAndPrioritize") 991 utils.MustMatch(t, tcase.out2.value(), got2.value(), "makeNumericAndPrioritize") 992 }) 993 } 994 } 995 996 func TestToSqlValue(t *testing.T) { 997 tcases := []struct { 998 typ sqltypes.Type 999 v EvalResult 1000 out sqltypes.Value 1001 err error 1002 }{{ 1003 typ: sqltypes.Int64, 1004 v: newEvalInt64(1), 1005 out: NewInt64(1), 1006 }, { 1007 typ: sqltypes.Int64, 1008 v: newEvalUint64(1), 1009 out: NewInt64(1), 1010 }, { 1011 typ: sqltypes.Int64, 1012 v: newEvalFloat(1.2e-16), 1013 out: NewInt64(0), 1014 }, { 1015 typ: sqltypes.Uint64, 1016 v: newEvalInt64(1), 1017 out: NewUint64(1), 1018 }, { 1019 typ: sqltypes.Uint64, 1020 v: newEvalUint64(1), 1021 out: NewUint64(1), 1022 }, { 1023 typ: sqltypes.Uint64, 1024 v: newEvalFloat(1.2e-16), 1025 out: NewUint64(0), 1026 }, { 1027 typ: sqltypes.Float64, 1028 v: newEvalInt64(1), 1029 out: TestValue(sqltypes.Float64, "1"), 1030 }, { 1031 typ: sqltypes.Float64, 1032 v: newEvalUint64(1), 1033 out: TestValue(sqltypes.Float64, "1"), 1034 }, { 1035 typ: sqltypes.Float64, 1036 v: newEvalFloat(1.2e-16), 1037 out: TestValue(sqltypes.Float64, "1.2e-16"), 1038 }, { 1039 typ: sqltypes.Decimal, 1040 v: newEvalInt64(1), 1041 out: TestValue(sqltypes.Decimal, "1"), 1042 }, { 1043 typ: sqltypes.Decimal, 1044 v: newEvalUint64(1), 1045 out: TestValue(sqltypes.Decimal, "1"), 1046 }, { 1047 // For float, we should not use scientific notation. 1048 typ: sqltypes.Decimal, 1049 v: newEvalFloat(1.2e-16), 1050 out: TestValue(sqltypes.Decimal, "0.00000000000000012"), 1051 }} 1052 for _, tcase := range tcases { 1053 got := tcase.v.toSQLValue(tcase.typ) 1054 1055 if !reflect.DeepEqual(got, tcase.out) { 1056 t.Errorf("toSQLValue(%v, %v): %v, want %v", tcase.v, tcase.typ, printValue(got), printValue(tcase.out)) 1057 } 1058 } 1059 } 1060 1061 func TestCompareNumeric(t *testing.T) { 1062 values := []EvalResult{ 1063 newEvalInt64(1), 1064 newEvalInt64(-1), 1065 newEvalInt64(0), 1066 newEvalInt64(2), 1067 newEvalUint64(1), 1068 newEvalUint64(0), 1069 newEvalUint64(2), 1070 newEvalFloat(1.0), 1071 newEvalFloat(-1.0), 1072 newEvalFloat(0.0), 1073 newEvalFloat(2.0), 1074 } 1075 1076 // cmpResults is a 2D array with the comparison expectations if we compare all values with each other 1077 cmpResults := [][]int{ 1078 // LHS -> 1 -1 0 2 u1 u0 u2 1.0 -1.0 0.0 2.0 1079 /*RHS 1*/ {0, 1, 1, -1, 0, 1, -1, 0, 1, 1, -1}, 1080 /* -1*/ {-1, 0, -1, -1, -1, -1, -1, -1, 0, -1, -1}, 1081 /* 0*/ {-1, 1, 0, -1, -1, 0, -1, -1, 1, 0, -1}, 1082 /* 2*/ {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0}, 1083 /* u1*/ {0, 1, 1, -1, 0, 1, -1, 0, 1, 1, -1}, 1084 /* u0*/ {-1, 1, 0, -1, -1, 0, -1, -1, 1, 0, -1}, 1085 /* u2*/ {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0}, 1086 /* 1.0*/ {0, 1, 1, -1, 0, 1, -1, 0, 1, 1, -1}, 1087 /* -1.0*/ {-1, 0, -1, -1, -1, -1, -1, -1, 0, -1, -1}, 1088 /* 0.0*/ {-1, 1, 0, -1, -1, 0, -1, -1, 1, 0, -1}, 1089 /* 2.0*/ {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0}, 1090 } 1091 1092 for aIdx, aVal := range values { 1093 for bIdx, bVal := range values { 1094 t.Run(fmt.Sprintf("[%d/%d] %s %s", aIdx, bIdx, aVal.debugString(), bVal.debugString()), func(t *testing.T) { 1095 result, err := compareNumeric(&aVal, &bVal) 1096 require.NoError(t, err) 1097 assert.Equal(t, cmpResults[aIdx][bIdx], result) 1098 1099 // if two values are considered equal, they must also produce the same hashcode 1100 if result == 0 { 1101 if aVal.typeof() == bVal.typeof() { 1102 // hash codes can only be compared if they are coerced to the same type first 1103 aHash, _ := aVal.nullSafeHashcode() 1104 bHash, _ := bVal.nullSafeHashcode() 1105 assert.Equal(t, aHash, bHash, "hash code does not match") 1106 } 1107 } 1108 }) 1109 } 1110 } 1111 } 1112 1113 func TestMin(t *testing.T) { 1114 tcases := []struct { 1115 v1, v2 sqltypes.Value 1116 min sqltypes.Value 1117 err error 1118 }{{ 1119 v1: NULL, 1120 v2: NULL, 1121 min: NULL, 1122 }, { 1123 v1: NewInt64(1), 1124 v2: NULL, 1125 min: NewInt64(1), 1126 }, { 1127 v1: NULL, 1128 v2: NewInt64(1), 1129 min: NewInt64(1), 1130 }, { 1131 v1: NewInt64(1), 1132 v2: NewInt64(2), 1133 min: NewInt64(1), 1134 }, { 1135 v1: NewInt64(2), 1136 v2: NewInt64(1), 1137 min: NewInt64(1), 1138 }, { 1139 v1: NewInt64(1), 1140 v2: NewInt64(1), 1141 min: NewInt64(1), 1142 }, { 1143 v1: TestValue(sqltypes.VarChar, "aa"), 1144 v2: TestValue(sqltypes.VarChar, "aa"), 1145 err: vterrors.New(vtrpcpb.Code_UNKNOWN, "cannot compare strings, collation is unknown or unsupported (collation ID: 0)"), 1146 }} 1147 for _, tcase := range tcases { 1148 v, err := Min(tcase.v1, tcase.v2, collations.Unknown) 1149 if !vterrors.Equals(err, tcase.err) { 1150 t.Errorf("Min error: %v, want %v", vterrors.Print(err), vterrors.Print(tcase.err)) 1151 } 1152 if tcase.err != nil { 1153 continue 1154 } 1155 1156 if !reflect.DeepEqual(v, tcase.min) { 1157 t.Errorf("Min(%v, %v): %v, want %v", tcase.v1, tcase.v2, v, tcase.min) 1158 } 1159 } 1160 } 1161 1162 func TestMinCollate(t *testing.T) { 1163 tcases := []struct { 1164 v1, v2 string 1165 collation collations.ID 1166 out string 1167 err error 1168 }{ 1169 { 1170 // accent insensitive 1171 v1: "ǍḄÇ", 1172 v2: "ÁḆĈ", 1173 out: "ǍḄÇ", 1174 collation: getCollationID("utf8mb4_0900_as_ci"), 1175 }, 1176 { 1177 // kana sensitive 1178 v1: "\xE3\x81\xAB\xE3\x81\xBB\xE3\x82\x93\xE3\x81\x94", 1179 v2: "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4", 1180 out: "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4", 1181 collation: getCollationID("utf8mb4_ja_0900_as_cs_ks"), 1182 }, 1183 { 1184 // non breaking space 1185 v1: "abc ", 1186 v2: "abc\u00a0", 1187 out: "abc\u00a0", 1188 collation: getCollationID("utf8mb4_0900_as_cs"), 1189 }, 1190 { 1191 // "cs" counts as a separate letter, where c < cs < d 1192 v1: "c", 1193 v2: "cs", 1194 out: "cs", 1195 collation: getCollationID("utf8mb4_hu_0900_ai_ci"), 1196 }, 1197 { 1198 // "cs" counts as a separate letter, where c < cs < d 1199 v1: "cukor", 1200 v2: "csak", 1201 out: "csak", 1202 collation: getCollationID("utf8mb4_hu_0900_ai_ci"), 1203 }, 1204 } 1205 for _, tcase := range tcases { 1206 got, err := Min(TestValue(sqltypes.VarChar, tcase.v1), TestValue(sqltypes.VarChar, tcase.v2), tcase.collation) 1207 if !vterrors.Equals(err, tcase.err) { 1208 t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", tcase.v1, tcase.v2, vterrors.Print(err), vterrors.Print(tcase.err)) 1209 } 1210 if tcase.err != nil { 1211 continue 1212 } 1213 1214 if got.ToString() == tcase.out { 1215 t.Errorf("NullsafeCompare(%v, %v): %v, want %v", tcase.v1, tcase.v2, got, tcase.out) 1216 } 1217 } 1218 } 1219 1220 func TestMax(t *testing.T) { 1221 tcases := []struct { 1222 v1, v2 sqltypes.Value 1223 max sqltypes.Value 1224 err error 1225 }{{ 1226 v1: NULL, 1227 v2: NULL, 1228 max: NULL, 1229 }, { 1230 v1: NewInt64(1), 1231 v2: NULL, 1232 max: NewInt64(1), 1233 }, { 1234 v1: NULL, 1235 v2: NewInt64(1), 1236 max: NewInt64(1), 1237 }, { 1238 v1: NewInt64(1), 1239 v2: NewInt64(2), 1240 max: NewInt64(2), 1241 }, { 1242 v1: NewInt64(2), 1243 v2: NewInt64(1), 1244 max: NewInt64(2), 1245 }, { 1246 v1: NewInt64(1), 1247 v2: NewInt64(1), 1248 max: NewInt64(1), 1249 }, { 1250 v1: TestValue(sqltypes.VarChar, "aa"), 1251 v2: TestValue(sqltypes.VarChar, "aa"), 1252 err: vterrors.New(vtrpcpb.Code_UNKNOWN, "cannot compare strings, collation is unknown or unsupported (collation ID: 0)"), 1253 }} 1254 for _, tcase := range tcases { 1255 v, err := Max(tcase.v1, tcase.v2, collations.Unknown) 1256 if !vterrors.Equals(err, tcase.err) { 1257 t.Errorf("Max error: %v, want %v", vterrors.Print(err), vterrors.Print(tcase.err)) 1258 } 1259 if tcase.err != nil { 1260 continue 1261 } 1262 1263 if !reflect.DeepEqual(v, tcase.max) { 1264 t.Errorf("Max(%v, %v): %v, want %v", tcase.v1, tcase.v2, v, tcase.max) 1265 } 1266 } 1267 } 1268 1269 func TestMaxCollate(t *testing.T) { 1270 tcases := []struct { 1271 v1, v2 string 1272 collation collations.ID 1273 out string 1274 err error 1275 }{ 1276 { 1277 // accent insensitive 1278 v1: "ǍḄÇ", 1279 v2: "ÁḆĈ", 1280 out: "ǍḄÇ", 1281 collation: getCollationID("utf8mb4_0900_as_ci"), 1282 }, 1283 { 1284 // kana sensitive 1285 v1: "\xE3\x81\xAB\xE3\x81\xBB\xE3\x82\x93\xE3\x81\x94", 1286 v2: "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4", 1287 out: "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4", 1288 collation: getCollationID("utf8mb4_ja_0900_as_cs_ks"), 1289 }, 1290 { 1291 // non breaking space 1292 v1: "abc ", 1293 v2: "abc\u00a0", 1294 out: "abc\u00a0", 1295 collation: getCollationID("utf8mb4_0900_as_cs"), 1296 }, 1297 { 1298 // "cs" counts as a separate letter, where c < cs < d 1299 v1: "c", 1300 v2: "cs", 1301 out: "cs", 1302 collation: getCollationID("utf8mb4_hu_0900_ai_ci"), 1303 }, 1304 { 1305 // "cs" counts as a separate letter, where c < cs < d 1306 v1: "cukor", 1307 v2: "csak", 1308 out: "csak", 1309 collation: getCollationID("utf8mb4_hu_0900_ai_ci"), 1310 }, 1311 } 1312 for _, tcase := range tcases { 1313 got, err := Max(TestValue(sqltypes.VarChar, tcase.v1), TestValue(sqltypes.VarChar, tcase.v2), tcase.collation) 1314 if !vterrors.Equals(err, tcase.err) { 1315 t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", tcase.v1, tcase.v2, vterrors.Print(err), vterrors.Print(tcase.err)) 1316 } 1317 if tcase.err != nil { 1318 continue 1319 } 1320 1321 if got.ToString() != tcase.out { 1322 t.Errorf("NullsafeCompare(%v, %v): %v, want %v", tcase.v1, tcase.v2, got, tcase.out) 1323 } 1324 } 1325 } 1326 1327 func printValue(v sqltypes.Value) string { 1328 vBytes, _ := v.ToBytes() 1329 return fmt.Sprintf("%v:%q", v.Type(), vBytes) 1330 } 1331 1332 // These benchmarks show that using existing ASCII representations 1333 // for numbers is about 6x slower than using native representations. 1334 // However, 229ns is still a negligible time compared to the cost of 1335 // other operations. The additional complexity of introducing native 1336 // types is currently not worth it. So, we'll stay with the existing 1337 // ASCII representation for now. Using interfaces is more expensive 1338 // than native representation of values. This is probably because 1339 // interfaces also allocate memory, and also perform type assertions. 1340 // Actual benchmark is based on NoNative. So, the numbers are similar. 1341 // Date: 6/4/17 1342 // Version: go1.8 1343 // BenchmarkAddActual-8 10000000 263 ns/op 1344 // BenchmarkAddNoNative-8 10000000 228 ns/op 1345 // BenchmarkAddNative-8 50000000 40.0 ns/op 1346 // BenchmarkAddGoInterface-8 30000000 52.4 ns/op 1347 // BenchmarkAddGoNonInterface-8 2000000000 1.00 ns/op 1348 // BenchmarkAddGo-8 2000000000 1.00 ns/op 1349 func BenchmarkAddActual(b *testing.B) { 1350 v1 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")) 1351 v2 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("12")) 1352 for i := 0; i < b.N; i++ { 1353 v1, _ = NullSafeAdd(v1, v2, sqltypes.Int64) 1354 } 1355 } 1356 1357 func BenchmarkAddNoNative(b *testing.B) { 1358 v1 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")) 1359 v2 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("12")) 1360 for i := 0; i < b.N; i++ { 1361 iv1, _ := ToInt64(v1) 1362 iv2, _ := ToInt64(v2) 1363 v1 = sqltypes.MakeTrusted(sqltypes.Int64, strconv.AppendInt(nil, iv1+iv2, 10)) 1364 } 1365 } 1366 1367 func BenchmarkAddNative(b *testing.B) { 1368 v1 := makeNativeInt64(1) 1369 v2 := makeNativeInt64(12) 1370 for i := 0; i < b.N; i++ { 1371 iv1 := int64(binary.BigEndian.Uint64(v1.Raw())) 1372 iv2 := int64(binary.BigEndian.Uint64(v2.Raw())) 1373 v1 = makeNativeInt64(iv1 + iv2) 1374 } 1375 } 1376 1377 func makeNativeInt64(v int64) sqltypes.Value { 1378 buf := make([]byte, 8) 1379 binary.BigEndian.PutUint64(buf, uint64(v)) 1380 return sqltypes.MakeTrusted(sqltypes.Int64, buf) 1381 } 1382 1383 func BenchmarkAddGoInterface(b *testing.B) { 1384 var v1, v2 any 1385 v1 = int64(1) 1386 v2 = int64(2) 1387 for i := 0; i < b.N; i++ { 1388 v1 = v1.(int64) + v2.(int64) 1389 } 1390 } 1391 1392 func BenchmarkAddGoNonInterface(b *testing.B) { 1393 v1 := newEvalInt64(1) 1394 v2 := newEvalInt64(12) 1395 for i := 0; i < b.N; i++ { 1396 if v1.typeof() != sqltypes.Int64 { 1397 b.Error("type assertion failed") 1398 } 1399 if v2.typeof() != sqltypes.Int64 { 1400 b.Error("type assertion failed") 1401 } 1402 v1 = newEvalInt64(v1.int64() + v2.int64()) 1403 } 1404 } 1405 1406 func BenchmarkAddGo(b *testing.B) { 1407 v1 := int64(1) 1408 v2 := int64(2) 1409 for i := 0; i < b.N; i++ { 1410 v1 += v2 1411 } 1412 } 1413 1414 func TestParseStringToFloat(t *testing.T) { 1415 tcs := []struct { 1416 str string 1417 val float64 1418 }{ 1419 {str: ""}, 1420 {str: " "}, 1421 {str: "1", val: 1}, 1422 {str: "1.10", val: 1.10}, 1423 {str: " 6.87", val: 6.87}, 1424 {str: "93.66 ", val: 93.66}, 1425 {str: "\t 42.10 \n ", val: 42.10}, 1426 {str: "1.10aa", val: 1.10}, 1427 {str: ".", val: 0.00}, 1428 {str: ".99", val: 0.99}, 1429 {str: "..99", val: 0}, 1430 {str: "1.", val: 1}, 1431 {str: "0.1.99", val: 0.1}, 1432 {str: "0.", val: 0}, 1433 {str: "8794354", val: 8794354}, 1434 {str: " 10 ", val: 10}, 1435 {str: "2266951196291479516", val: 2266951196291479516}, 1436 {str: "abcd123", val: 0}, 1437 } 1438 1439 for _, tc := range tcs { 1440 t.Run(tc.str, func(t *testing.T) { 1441 got := parseStringToFloat(tc.str) 1442 require.EqualValues(t, tc.val, got) 1443 }) 1444 } 1445 }