storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/s3select/sql/value_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2019 MinIO, Inc. 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 sql 18 19 import ( 20 "fmt" 21 "math" 22 "strconv" 23 "testing" 24 "time" 25 ) 26 27 // valueBuilders contains one constructor for each value type. 28 // Values should match if type is the same. 29 var valueBuilders = []func() *Value{ 30 func() *Value { 31 return FromNull() 32 }, 33 func() *Value { 34 return FromBool(true) 35 }, 36 func() *Value { 37 return FromBytes([]byte("byte contents")) 38 }, 39 func() *Value { 40 return FromFloat(math.Pi) 41 }, 42 func() *Value { 43 return FromInt(0x1337) 44 }, 45 func() *Value { 46 t, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") 47 if err != nil { 48 panic(err) 49 } 50 return FromTimestamp(t) 51 }, 52 func() *Value { 53 return FromString("string contents") 54 }, 55 } 56 57 // altValueBuilders contains one constructor for each value type. 58 // Values are zero values and should NOT match the values in valueBuilders, except Null type. 59 var altValueBuilders = []func() *Value{ 60 func() *Value { 61 return FromNull() 62 }, 63 func() *Value { 64 return FromBool(false) 65 }, 66 func() *Value { 67 return FromBytes(nil) 68 }, 69 func() *Value { 70 return FromFloat(0) 71 }, 72 func() *Value { 73 return FromInt(0) 74 }, 75 func() *Value { 76 return FromTimestamp(time.Time{}) 77 }, 78 func() *Value { 79 return FromString("") 80 }, 81 } 82 83 func TestValue_SameTypeAs(t *testing.T) { 84 type fields struct { 85 a, b Value 86 } 87 type test struct { 88 name string 89 fields fields 90 wantOk bool 91 } 92 var tests []test 93 for i := range valueBuilders { 94 a := valueBuilders[i]() 95 for j := range valueBuilders { 96 b := valueBuilders[j]() 97 tests = append(tests, test{ 98 name: fmt.Sprint(a.GetTypeString(), "==", b.GetTypeString()), 99 fields: fields{ 100 a: *a, b: *b, 101 }, 102 wantOk: i == j, 103 }) 104 } 105 } 106 107 for _, tt := range tests { 108 t.Run(tt.name, func(t *testing.T) { 109 if gotOk := tt.fields.a.SameTypeAs(tt.fields.b); gotOk != tt.wantOk { 110 t.Errorf("SameTypeAs() = %v, want %v", gotOk, tt.wantOk) 111 } 112 }) 113 } 114 } 115 116 func TestValue_Equals(t *testing.T) { 117 type fields struct { 118 a, b Value 119 } 120 type test struct { 121 name string 122 fields fields 123 wantOk bool 124 } 125 var tests []test 126 for i := range valueBuilders { 127 a := valueBuilders[i]() 128 for j := range valueBuilders { 129 b := valueBuilders[j]() 130 tests = append(tests, test{ 131 name: fmt.Sprint(a.GetTypeString(), "==", b.GetTypeString()), 132 fields: fields{ 133 a: *a, b: *b, 134 }, 135 wantOk: i == j, 136 }) 137 } 138 } 139 for i := range valueBuilders { 140 a := valueBuilders[i]() 141 for j := range altValueBuilders { 142 b := altValueBuilders[j]() 143 tests = append(tests, test{ 144 name: fmt.Sprint(a.GetTypeString(), "!=", b.GetTypeString()), 145 fields: fields{ 146 a: *a, b: *b, 147 }, 148 // Only Null == Null 149 wantOk: a.IsNull() && b.IsNull() && i == 0 && j == 0, 150 }) 151 } 152 } 153 for _, tt := range tests { 154 t.Run(tt.name, func(t *testing.T) { 155 if gotOk := tt.fields.a.Equals(tt.fields.b); gotOk != tt.wantOk { 156 t.Errorf("Equals() = %v, want %v", gotOk, tt.wantOk) 157 } 158 }) 159 } 160 } 161 162 func TestValue_CSVString(t *testing.T) { 163 type test struct { 164 name string 165 want string 166 wantAlt string 167 } 168 169 tests := []test{ 170 { 171 name: valueBuilders[0]().String(), 172 want: "", 173 wantAlt: "", 174 }, 175 { 176 name: valueBuilders[1]().String(), 177 want: "true", 178 wantAlt: "false", 179 }, 180 { 181 name: valueBuilders[2]().String(), 182 want: "byte contents", 183 wantAlt: "", 184 }, 185 { 186 name: valueBuilders[3]().String(), 187 want: "3.141592653589793", 188 wantAlt: "0", 189 }, 190 { 191 name: valueBuilders[4]().String(), 192 want: "4919", 193 wantAlt: "0", 194 }, 195 { 196 name: valueBuilders[5]().String(), 197 want: "2006-01-02T15:04:05Z", 198 wantAlt: "0001T", 199 }, 200 { 201 name: valueBuilders[6]().String(), 202 want: "string contents", 203 wantAlt: "", 204 }, 205 } 206 207 for i, tt := range tests { 208 t.Run(tt.name, func(t *testing.T) { 209 v := valueBuilders[i]() 210 vAlt := altValueBuilders[i]() 211 if got := v.CSVString(); got != tt.want { 212 t.Errorf("CSVString() = %v, want %v", got, tt.want) 213 } 214 if got := vAlt.CSVString(); got != tt.wantAlt { 215 t.Errorf("CSVString() = %v, want %v", got, tt.wantAlt) 216 } 217 }) 218 } 219 } 220 221 func TestValue_bytesToInt(t *testing.T) { 222 type fields struct { 223 value interface{} 224 } 225 tests := []struct { 226 name string 227 fields fields 228 want int64 229 wantOK bool 230 }{ 231 { 232 name: "zero", 233 fields: fields{ 234 value: []byte("0"), 235 }, 236 want: 0, 237 wantOK: true, 238 }, 239 { 240 name: "minuszero", 241 fields: fields{ 242 value: []byte("-0"), 243 }, 244 want: 0, 245 wantOK: true, 246 }, 247 { 248 name: "one", 249 fields: fields{ 250 value: []byte("1"), 251 }, 252 want: 1, 253 wantOK: true, 254 }, 255 { 256 name: "minusone", 257 fields: fields{ 258 value: []byte("-1"), 259 }, 260 want: -1, 261 wantOK: true, 262 }, 263 { 264 name: "plusone", 265 fields: fields{ 266 value: []byte("+1"), 267 }, 268 want: 1, 269 wantOK: true, 270 }, 271 { 272 name: "max", 273 fields: fields{ 274 value: []byte(strconv.FormatInt(math.MaxInt64, 10)), 275 }, 276 want: math.MaxInt64, 277 wantOK: true, 278 }, 279 { 280 name: "min", 281 fields: fields{ 282 value: []byte(strconv.FormatInt(math.MinInt64, 10)), 283 }, 284 want: math.MinInt64, 285 wantOK: true, 286 }, 287 { 288 name: "max-overflow", 289 fields: fields{ 290 value: []byte("9223372036854775808"), 291 }, 292 // Seems to be what strconv.ParseInt returns 293 want: math.MaxInt64, 294 wantOK: false, 295 }, 296 { 297 name: "min-underflow", 298 fields: fields{ 299 value: []byte("-9223372036854775809"), 300 }, 301 // Seems to be what strconv.ParseInt returns 302 want: math.MinInt64, 303 wantOK: false, 304 }, 305 { 306 name: "zerospace", 307 fields: fields{ 308 value: []byte(" 0"), 309 }, 310 want: 0, 311 wantOK: true, 312 }, 313 { 314 name: "onespace", 315 fields: fields{ 316 value: []byte("1 "), 317 }, 318 want: 1, 319 wantOK: true, 320 }, 321 { 322 name: "minusonespace", 323 fields: fields{ 324 value: []byte(" -1 "), 325 }, 326 want: -1, 327 wantOK: true, 328 }, 329 { 330 name: "plusonespace", 331 fields: fields{ 332 value: []byte("\t+1\t"), 333 }, 334 want: 1, 335 wantOK: true, 336 }, 337 { 338 name: "scientific", 339 fields: fields{ 340 value: []byte("3e5"), 341 }, 342 want: 0, 343 wantOK: false, 344 }, 345 { 346 // No support for prefixes 347 name: "hex", 348 fields: fields{ 349 value: []byte("0xff"), 350 }, 351 want: 0, 352 wantOK: false, 353 }, 354 } 355 for _, tt := range tests { 356 t.Run(tt.name, func(t *testing.T) { 357 v := &Value{ 358 value: tt.fields.value, 359 } 360 got, got1 := v.bytesToInt() 361 if got != tt.want { 362 t.Errorf("bytesToInt() got = %v, want %v", got, tt.want) 363 } 364 if got1 != tt.wantOK { 365 t.Errorf("bytesToInt() got1 = %v, want %v", got1, tt.wantOK) 366 } 367 }) 368 } 369 } 370 371 func TestValue_bytesToFloat(t *testing.T) { 372 type fields struct { 373 value interface{} 374 } 375 tests := []struct { 376 name string 377 fields fields 378 want float64 379 wantOK bool 380 }{ 381 // Copied from TestValue_bytesToInt. 382 { 383 name: "zero", 384 fields: fields{ 385 value: []byte("0"), 386 }, 387 want: 0, 388 wantOK: true, 389 }, 390 { 391 name: "minuszero", 392 fields: fields{ 393 value: []byte("-0"), 394 }, 395 want: 0, 396 wantOK: true, 397 }, 398 { 399 name: "one", 400 fields: fields{ 401 value: []byte("1"), 402 }, 403 want: 1, 404 wantOK: true, 405 }, 406 { 407 name: "minusone", 408 fields: fields{ 409 value: []byte("-1"), 410 }, 411 want: -1, 412 wantOK: true, 413 }, 414 { 415 name: "plusone", 416 fields: fields{ 417 value: []byte("+1"), 418 }, 419 want: 1, 420 wantOK: true, 421 }, 422 { 423 name: "maxint", 424 fields: fields{ 425 value: []byte(strconv.FormatInt(math.MaxInt64, 10)), 426 }, 427 want: math.MaxInt64, 428 wantOK: true, 429 }, 430 { 431 name: "minint", 432 fields: fields{ 433 value: []byte(strconv.FormatInt(math.MinInt64, 10)), 434 }, 435 want: math.MinInt64, 436 wantOK: true, 437 }, 438 { 439 name: "max-overflow-int", 440 fields: fields{ 441 value: []byte("9223372036854775808"), 442 }, 443 // Seems to be what strconv.ParseInt returns 444 want: math.MaxInt64, 445 wantOK: true, 446 }, 447 { 448 name: "min-underflow-int", 449 fields: fields{ 450 value: []byte("-9223372036854775809"), 451 }, 452 // Seems to be what strconv.ParseInt returns 453 want: math.MinInt64, 454 wantOK: true, 455 }, 456 { 457 name: "max", 458 fields: fields{ 459 value: []byte(strconv.FormatFloat(math.MaxFloat64, 'g', -1, 64)), 460 }, 461 want: math.MaxFloat64, 462 wantOK: true, 463 }, 464 { 465 name: "min", 466 fields: fields{ 467 value: []byte(strconv.FormatFloat(-math.MaxFloat64, 'g', -1, 64)), 468 }, 469 want: -math.MaxFloat64, 470 wantOK: true, 471 }, 472 { 473 name: "max-overflow", 474 fields: fields{ 475 value: []byte("1.797693134862315708145274237317043567981e+309"), 476 }, 477 // Seems to be what strconv.ParseInt returns 478 want: math.Inf(1), 479 wantOK: false, 480 }, 481 { 482 name: "min-underflow", 483 fields: fields{ 484 value: []byte("-1.797693134862315708145274237317043567981e+309"), 485 }, 486 // Seems to be what strconv.ParseInt returns 487 want: math.Inf(-1), 488 wantOK: false, 489 }, 490 { 491 name: "smallest-pos", 492 fields: fields{ 493 value: []byte(strconv.FormatFloat(math.SmallestNonzeroFloat64, 'g', -1, 64)), 494 }, 495 want: math.SmallestNonzeroFloat64, 496 wantOK: true, 497 }, 498 { 499 name: "smallest-pos", 500 fields: fields{ 501 value: []byte(strconv.FormatFloat(-math.SmallestNonzeroFloat64, 'g', -1, 64)), 502 }, 503 want: -math.SmallestNonzeroFloat64, 504 wantOK: true, 505 }, 506 { 507 name: "zerospace", 508 fields: fields{ 509 value: []byte(" 0"), 510 }, 511 want: 0, 512 wantOK: true, 513 }, 514 { 515 name: "onespace", 516 fields: fields{ 517 value: []byte("1 "), 518 }, 519 want: 1, 520 wantOK: true, 521 }, 522 { 523 name: "minusonespace", 524 fields: fields{ 525 value: []byte(" -1 "), 526 }, 527 want: -1, 528 wantOK: true, 529 }, 530 { 531 name: "plusonespace", 532 fields: fields{ 533 value: []byte("\t+1\t"), 534 }, 535 want: 1, 536 wantOK: true, 537 }, 538 { 539 name: "scientific", 540 fields: fields{ 541 value: []byte("3e5"), 542 }, 543 want: 300000, 544 wantOK: true, 545 }, 546 { 547 // No support for prefixes 548 name: "hex", 549 fields: fields{ 550 value: []byte("0xff"), 551 }, 552 want: 0, 553 wantOK: false, 554 }, 555 } 556 for _, tt := range tests { 557 t.Run(tt.name, func(t *testing.T) { 558 v := Value{ 559 value: tt.fields.value, 560 } 561 got, got1 := v.bytesToFloat() 562 diff := math.Abs(got - tt.want) 563 if diff > floatCmpTolerance { 564 t.Errorf("bytesToFloat() got = %v, want %v", got, tt.want) 565 } 566 if got1 != tt.wantOK { 567 t.Errorf("bytesToFloat() got1 = %v, want %v", got1, tt.wantOK) 568 } 569 }) 570 } 571 } 572 573 func TestValue_bytesToBool(t *testing.T) { 574 type fields struct { 575 value interface{} 576 } 577 tests := []struct { 578 name string 579 fields fields 580 wantVal bool 581 wantOk bool 582 }{ 583 { 584 name: "true", 585 fields: fields{ 586 value: []byte("true"), 587 }, 588 wantVal: true, 589 wantOk: true, 590 }, 591 { 592 name: "false", 593 fields: fields{ 594 value: []byte("false"), 595 }, 596 wantVal: false, 597 wantOk: true, 598 }, 599 { 600 name: "t", 601 fields: fields{ 602 value: []byte("t"), 603 }, 604 wantVal: true, 605 wantOk: true, 606 }, 607 { 608 name: "f", 609 fields: fields{ 610 value: []byte("f"), 611 }, 612 wantVal: false, 613 wantOk: true, 614 }, 615 { 616 name: "1", 617 fields: fields{ 618 value: []byte("1"), 619 }, 620 wantVal: true, 621 wantOk: true, 622 }, 623 { 624 name: "0", 625 fields: fields{ 626 value: []byte("0"), 627 }, 628 wantVal: false, 629 wantOk: true, 630 }, 631 { 632 name: "truespace", 633 fields: fields{ 634 value: []byte(" true "), 635 }, 636 wantVal: true, 637 wantOk: true, 638 }, 639 { 640 name: "truetabs", 641 fields: fields{ 642 value: []byte("\ttrue\t"), 643 }, 644 wantVal: true, 645 wantOk: true, 646 }, 647 { 648 name: "TRUE", 649 fields: fields{ 650 value: []byte("TRUE"), 651 }, 652 wantVal: true, 653 wantOk: true, 654 }, 655 { 656 name: "FALSE", 657 fields: fields{ 658 value: []byte("FALSE"), 659 }, 660 wantVal: false, 661 wantOk: true, 662 }, 663 { 664 name: "invalid", 665 fields: fields{ 666 value: []byte("no"), 667 }, 668 wantVal: false, 669 wantOk: false, 670 }, 671 } 672 for _, tt := range tests { 673 t.Run(tt.name, func(t *testing.T) { 674 v := Value{ 675 value: tt.fields.value, 676 } 677 gotVal, gotOk := v.bytesToBool() 678 if gotVal != tt.wantVal { 679 t.Errorf("bytesToBool() gotVal = %v, want %v", gotVal, tt.wantVal) 680 } 681 if gotOk != tt.wantOk { 682 t.Errorf("bytesToBool() gotOk = %v, want %v", gotOk, tt.wantOk) 683 } 684 }) 685 } 686 }