github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/table/scanner/scanner_test.go (about) 1 package scanner 2 3 import ( 4 "encoding/binary" 5 "encoding/json" 6 "math" 7 "strconv" 8 "testing" 9 "time" 10 11 "github.com/google/uuid" 12 "github.com/stretchr/testify/require" 13 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 14 15 "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xrand" 17 "github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed" 18 "github.com/ydb-platform/ydb-go-sdk/v3/table/result/named" 19 "github.com/ydb-platform/ydb-go-sdk/v3/table/types" 20 ) 21 22 //nolint:gocyclo 23 func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) { 24 rv := r.Int64(math.MaxInt16) 25 switch c.typeID { 26 case Ydb.Type_BOOL: 27 v := rv%2 == 1 28 ydbval := &Ydb.Value{ 29 Value: &Ydb.Value_BoolValue{ 30 BoolValue: v, 31 }, 32 } 33 if c.optional && !c.testDefault { 34 vp := &v 35 36 return ydbval, &vp 37 } 38 39 return ydbval, &v 40 case Ydb.Type_INT8: 41 v := int8(rv) 42 ydbval := &Ydb.Value{ 43 Value: &Ydb.Value_Int32Value{ 44 Int32Value: int32(v), 45 }, 46 } 47 if c.optional && !c.testDefault { 48 vp := &v 49 50 return ydbval, &vp 51 } 52 53 return ydbval, &v 54 case Ydb.Type_UINT8: 55 if c.nilValue { 56 ydbval := &Ydb.Value{ 57 Value: &Ydb.Value_NullFlagValue{}, 58 } 59 if c.testDefault { 60 var dv uint8 61 62 return ydbval, &dv 63 } 64 var dv *uint8 65 66 return ydbval, &dv 67 } 68 v := uint8(rv) 69 ydbval := &Ydb.Value{ 70 Value: &Ydb.Value_Uint32Value{ 71 Uint32Value: uint32(v), 72 }, 73 } 74 if c.optional && !c.testDefault { 75 vp := &v 76 77 return ydbval, &vp 78 } 79 80 return ydbval, &v 81 case Ydb.Type_INT16: 82 v := int16(rv) 83 ydbval := &Ydb.Value{ 84 Value: &Ydb.Value_Int32Value{ 85 Int32Value: int32(v), 86 }, 87 } 88 if c.optional && !c.testDefault { 89 vp := &v 90 91 return ydbval, &vp 92 } 93 94 return ydbval, &v 95 case Ydb.Type_UINT16: 96 v := uint16(rv) 97 ydbval := &Ydb.Value{ 98 Value: &Ydb.Value_Uint32Value{ 99 Uint32Value: uint32(v), 100 }, 101 } 102 if c.optional && !c.testDefault { 103 vp := &v 104 105 return ydbval, &vp 106 } 107 108 return ydbval, &v 109 case Ydb.Type_INT32: 110 if c.nilValue { 111 ydbval := &Ydb.Value{ 112 Value: &Ydb.Value_NullFlagValue{}, 113 } 114 if c.testDefault { 115 var dv int32 116 117 return ydbval, &dv 118 } 119 var dv *int32 120 121 return ydbval, &dv 122 } 123 v := int32(rv) 124 ydbval := &Ydb.Value{ 125 Value: &Ydb.Value_Int32Value{ 126 Int32Value: v, 127 }, 128 } 129 if c.optional && !c.testDefault { 130 vp := &v 131 132 return ydbval, &vp 133 } 134 135 return ydbval, &v 136 case Ydb.Type_UINT32: 137 v := uint32(rv) 138 ydbval := &Ydb.Value{ 139 Value: &Ydb.Value_Uint32Value{ 140 Uint32Value: v, 141 }, 142 } 143 if c.optional && !c.testDefault { 144 vp := &v 145 146 return ydbval, &vp 147 } 148 149 return ydbval, &v 150 case Ydb.Type_INT64: 151 v := rv 152 ydbval := &Ydb.Value{ 153 Value: &Ydb.Value_Int64Value{ 154 Int64Value: v, 155 }, 156 } 157 if c.ydbvalue { 158 vp := types.Int64Value(v) 159 160 return ydbval, &vp 161 } 162 if c.scanner { 163 s := intIncScanner(v + 10) 164 165 return ydbval, &s 166 } 167 if c.optional && !c.testDefault { 168 vp := &v 169 170 return ydbval, &vp 171 } 172 173 return ydbval, &v 174 case Ydb.Type_UINT64: 175 v := uint64(rv) 176 ydbval := &Ydb.Value{ 177 Value: &Ydb.Value_Uint64Value{ 178 Uint64Value: v, 179 }, 180 } 181 if c.optional && !c.testDefault { 182 vp := &v 183 184 return ydbval, &vp 185 } 186 187 return ydbval, &v 188 case Ydb.Type_FLOAT: 189 v := float32(rv) 190 ydbval := &Ydb.Value{ 191 Value: &Ydb.Value_FloatValue{ 192 FloatValue: v, 193 }, 194 } 195 if c.ydbvalue { 196 vp := types.FloatValue(v) 197 198 return ydbval, &vp 199 } 200 if c.optional && !c.testDefault { 201 vp := &v 202 203 return ydbval, &vp 204 } 205 206 return ydbval, &v 207 case Ydb.Type_DOUBLE: 208 v := float64(rv) 209 ydbval := &Ydb.Value{ 210 Value: &Ydb.Value_DoubleValue{ 211 DoubleValue: v, 212 }, 213 } 214 if c.optional && !c.testDefault { 215 vp := &v 216 217 return ydbval, &vp 218 } 219 220 return ydbval, &v 221 case Ydb.Type_DATE: 222 v := uint32(rv) 223 ydbval := &Ydb.Value{ 224 Value: &Ydb.Value_Uint32Value{ 225 Uint32Value: v, 226 }, 227 } 228 src := value.DateToTime(v) 229 if c.scanner { 230 s := dateScanner(src) 231 232 return ydbval, &s 233 } 234 if c.optional && !c.testDefault { 235 vp := &src 236 237 return ydbval, &vp 238 } 239 240 return ydbval, &src 241 case Ydb.Type_DATETIME: 242 v := uint32(rv) 243 ydbval := &Ydb.Value{ 244 Value: &Ydb.Value_Uint32Value{ 245 Uint32Value: v, 246 }, 247 } 248 src := value.DatetimeToTime(v) 249 if c.optional && !c.testDefault { 250 vp := &src 251 252 return ydbval, &vp 253 } 254 255 return ydbval, &src 256 case Ydb.Type_TIMESTAMP: 257 v := uint64(rv) 258 ydbval := &Ydb.Value{ 259 Value: &Ydb.Value_Uint64Value{ 260 Uint64Value: v, 261 }, 262 } 263 src := value.TimestampToTime(v) 264 if c.optional && !c.testDefault { 265 vp := &src 266 267 return ydbval, &vp 268 } 269 270 return ydbval, &src 271 case Ydb.Type_INTERVAL: 272 if c.nilValue { 273 ydbval := &Ydb.Value{ 274 Value: &Ydb.Value_NullFlagValue{}, 275 } 276 if c.testDefault { 277 var dv time.Duration 278 279 return ydbval, &dv 280 } 281 var dv *time.Duration 282 283 return ydbval, &dv 284 } 285 rv %= time.Now().Unix() 286 v := rv 287 ydbval := &Ydb.Value{ 288 Value: &Ydb.Value_Int64Value{ 289 Int64Value: v, 290 }, 291 } 292 src := value.IntervalToDuration(v) 293 if c.optional && !c.testDefault { 294 vp := &src 295 296 return ydbval, &vp 297 } 298 299 return ydbval, &src 300 case Ydb.Type_TZ_DATE: 301 v := time.Now().Format(value.LayoutDate) + ",Europe/Berlin" 302 ydbval := &Ydb.Value{ 303 Value: &Ydb.Value_TextValue{ 304 TextValue: v, 305 }, 306 } 307 src, _ := value.TzDateToTime(v) 308 if c.optional && !c.testDefault { 309 vp := &src 310 311 return ydbval, &vp 312 } 313 314 return ydbval, &src 315 case Ydb.Type_TZ_DATETIME: 316 if c.nilValue { 317 ydbval := &Ydb.Value{ 318 Value: &Ydb.Value_NullFlagValue{}, 319 } 320 if c.testDefault { 321 var dv time.Time 322 323 return ydbval, &dv 324 } 325 var dv *time.Time 326 327 return ydbval, &dv 328 } 329 rv %= time.Now().Unix() 330 v := value.DatetimeToTime(uint32(rv)).Format(value.LayoutTzDatetime) + ",Europe/Berlin" 331 ydbval := &Ydb.Value{ 332 Value: &Ydb.Value_TextValue{ 333 TextValue: v, 334 }, 335 } 336 src, _ := value.TzDatetimeToTime(v) 337 if c.optional && !c.testDefault { 338 vp := &src 339 340 return ydbval, &vp 341 } 342 343 return ydbval, &src 344 case Ydb.Type_TZ_TIMESTAMP: 345 rv %= time.Now().Unix() 346 v := value.TimestampToTime(uint64(rv)).Format(value.LayoutTzTimestamp) + ",Europe/Berlin" 347 ydbval := &Ydb.Value{ 348 Value: &Ydb.Value_TextValue{ 349 TextValue: v, 350 }, 351 } 352 src, _ := value.TzTimestampToTime(v) 353 if c.optional && !c.testDefault { 354 vp := &src 355 356 return ydbval, &vp 357 } 358 359 return ydbval, &src 360 case Ydb.Type_STRING: 361 if c.nilValue { 362 ydbval := &Ydb.Value{ 363 Value: &Ydb.Value_NullFlagValue{}, 364 } 365 if c.testDefault { 366 var dv []byte 367 368 return ydbval, &dv 369 } 370 var dv *[]byte 371 372 return ydbval, &dv 373 } 374 v := make([]byte, 16) 375 binary.BigEndian.PutUint64(v[0:8], uint64(rv)) 376 binary.BigEndian.PutUint64(v[8:16], uint64(rv)) 377 ydbval := &Ydb.Value{ 378 Value: &Ydb.Value_BytesValue{ 379 BytesValue: v, 380 }, 381 } 382 src := v 383 if c.optional && !c.testDefault { 384 vp := &src 385 386 return ydbval, &vp 387 } 388 389 return ydbval, &src 390 case Ydb.Type_UTF8: 391 v := strconv.FormatUint(uint64(rv), 10) 392 ydbval := &Ydb.Value{ 393 Value: &Ydb.Value_TextValue{ 394 TextValue: v, 395 }, 396 } 397 if c.optional && !c.testDefault { 398 vp := &v 399 400 return ydbval, &vp 401 } 402 403 return ydbval, &v 404 case Ydb.Type_YSON: 405 if c.nilValue { 406 ydbval := &Ydb.Value{ 407 Value: &Ydb.Value_NullFlagValue{}, 408 } 409 if c.testDefault { 410 var dv []byte 411 412 return ydbval, &dv 413 } 414 var dv *[]byte 415 416 return ydbval, &dv 417 } 418 v := strconv.FormatUint(uint64(rv), 10) 419 ydbval := &Ydb.Value{ 420 Value: &Ydb.Value_TextValue{ 421 TextValue: v, 422 }, 423 } 424 src := []byte(v) 425 if c.optional && !c.testDefault { 426 vp := &src 427 428 return ydbval, &vp 429 } 430 431 return ydbval, &src 432 case Ydb.Type_JSON: 433 v := strconv.FormatUint(uint64(rv), 10) 434 ydbval := &Ydb.Value{ 435 Value: &Ydb.Value_TextValue{ 436 TextValue: v, 437 }, 438 } 439 if c.ydbvalue { 440 vp := types.JSONValue(v) 441 442 return ydbval, &vp 443 } 444 src := []byte(v) 445 if c.optional && !c.testDefault { 446 vp := &src 447 448 return ydbval, &vp 449 } 450 451 return ydbval, &src 452 case Ydb.Type_UUID: 453 if c.nilValue { 454 ydbval := &Ydb.Value{ 455 Value: &Ydb.Value_NullFlagValue{}, 456 } 457 if c.testDefault { 458 var dv uuid.UUID 459 460 return ydbval, &dv 461 } 462 var dv *uuid.UUID 463 464 return ydbval, &dv 465 } 466 v := uuid.UUID{} 467 468 binary.LittleEndian.PutUint64(v[0:8], uint64(rv)) 469 binary.LittleEndian.PutUint64(v[8:16], uint64(rv)) 470 low, high := value.UUIDToHiLoPair(v) 471 ydbval := &Ydb.Value{ 472 High_128: high, 473 Value: &Ydb.Value_Low_128{ 474 Low_128: low, 475 }, 476 } 477 if c.optional && !c.testDefault { 478 vp := &v 479 480 return ydbval, &vp 481 } 482 483 return ydbval, &v 484 case Ydb.Type_JSON_DOCUMENT: 485 v := strconv.FormatUint(uint64(rv), 10) 486 ydbval := &Ydb.Value{ 487 Value: &Ydb.Value_TextValue{ 488 TextValue: v, 489 }, 490 } 491 src := []byte(v) 492 if c.optional && !c.testDefault { 493 vp := &src 494 495 return ydbval, &vp 496 } 497 498 return ydbval, &src 499 case Ydb.Type_DYNUMBER: 500 v := strconv.FormatUint(uint64(rv), 10) 501 ydbval := &Ydb.Value{ 502 Value: &Ydb.Value_TextValue{ 503 TextValue: v, 504 }, 505 } 506 if c.optional && !c.testDefault { 507 vp := &v 508 509 return ydbval, &vp 510 } 511 512 return ydbval, &v 513 default: 514 panic("ydb: unexpected types") 515 } 516 } 517 518 func getResultSet(count int, col []*column) (result *Ydb.ResultSet, testValues [][]indexed.RequiredOrOptional) { 519 result = &Ydb.ResultSet{} 520 for _, c := range col { 521 t := &Ydb.Type{ 522 Type: &Ydb.Type_TypeId{ 523 TypeId: c.typeID, 524 }, 525 } 526 if c.optional { 527 t = &Ydb.Type{ 528 Type: &Ydb.Type_OptionalType{ 529 OptionalType: &Ydb.OptionalType{ 530 Item: t, 531 }, 532 }, 533 } 534 } 535 result.Columns = append( 536 result.GetColumns(), 537 &Ydb.Column{ 538 Name: c.name, 539 Type: t, 540 }, 541 ) 542 } 543 544 r := xrand.New(xrand.WithLock()) 545 testValues = make([][]indexed.RequiredOrOptional, count) 546 for i := 0; i < count; i++ { 547 var items []*Ydb.Value 548 var vals []indexed.RequiredOrOptional 549 for j := range result.GetColumns() { 550 v, val := valueFromPrimitiveTypeID(col[j], r) 551 vals = append(vals, val) 552 items = append(items, v) 553 } 554 result.Rows = append(result.GetRows(), &Ydb.Value{ 555 Items: items, 556 }) 557 testValues[i] = vals 558 } 559 560 return result, testValues 561 } 562 563 func TestScanSqlTypes(t *testing.T) { 564 s := initScanner() 565 for _, test := range scannerData { 566 t.Run(test.name, func(t *testing.T) { 567 set, expected := getResultSet(test.count, test.columns) 568 s.reset(set, test.setColumns...) 569 for s.NextRow() { 570 if test.columns[0].testDefault { 571 if err := s.ScanWithDefaults(func() (values []indexed.Required) { 572 for _, v := range test.values { 573 values = append(values, v) 574 } 575 576 return values 577 }()...); err != nil { 578 t.Fatalf("test: %s; error: %s", test.name, err) 579 } 580 } else { 581 if err := s.Scan(test.values...); err != nil { 582 t.Fatalf("test: %s; error: %s", test.name, err) 583 } 584 } 585 if test.setColumnIndexes != nil { 586 for i, v := range test.setColumnIndexes { 587 require.Equal(t, expected[0][v], test.values[i]) 588 } 589 } else { 590 require.Equal(t, expected[0], test.values) 591 } 592 expected = expected[1:] 593 } 594 }) 595 } 596 } 597 598 func TestScanNamed(t *testing.T) { 599 s := initScanner() 600 or := func(columns []string, i int, defaultValue string) string { 601 if columns == nil { 602 return defaultValue 603 } 604 605 return columns[i] 606 } 607 for _, test := range scannerData { 608 t.Run(test.name, func(t *testing.T) { 609 set, expected := getResultSet(test.count, test.columns) 610 s.reset(set) 611 for s.NextRow() { 612 values := make([]named.Value, 0, len(test.values)) 613 //nolint:nestif 614 if test.columns[0].testDefault { 615 for i := range test.values { 616 values = append( 617 values, 618 named.OptionalWithDefault( 619 or(test.setColumns, i, test.columns[i].name), 620 test.values[i], 621 ), 622 ) 623 } 624 if err := s.ScanNamed(values...); err != nil { 625 t.Fatalf("test: %s; error: %s", test.name, err) 626 } 627 } else { 628 for i := range test.values { 629 if test.columns[i].optional { 630 if test.columns[i].testDefault { 631 values = append( 632 values, 633 named.OptionalWithDefault( 634 or(test.setColumns, i, test.columns[i].name), 635 test.values[i], 636 ), 637 ) 638 } else { 639 values = append( 640 values, 641 named.Optional( 642 or(test.setColumns, i, test.columns[i].name), 643 test.values[i], 644 ), 645 ) 646 } 647 } else { 648 values = append( 649 values, 650 named.Required( 651 or(test.setColumns, i, test.columns[i].name), 652 test.values[i], 653 ), 654 ) 655 } 656 } 657 if err := s.ScanNamed(values...); err != nil { 658 t.Fatalf("test: %s; error: %s", test.name, err) 659 } 660 } 661 if test.setColumnIndexes != nil { 662 for i, v := range test.setColumnIndexes { 663 require.Equal(t, expected[0][v], test.values[i]) 664 } 665 } else { 666 require.Equal(t, expected[0], test.values) 667 } 668 expected = expected[1:] 669 } 670 }) 671 } 672 } 673 674 type jsonUnmarshaller struct { 675 bytes []byte 676 } 677 678 func (json *jsonUnmarshaller) UnmarshalJSON(bytes []byte) error { 679 json.bytes = bytes 680 681 return nil 682 } 683 684 var _ json.Unmarshaler = &jsonUnmarshaller{} 685 686 func TestScanToJsonUnmarshaller(t *testing.T) { 687 s := initScanner() 688 for _, test := range []struct { 689 name string 690 count int 691 columns []*column 692 values []indexed.RequiredOrOptional 693 }{ 694 { 695 name: "<optional JSONDocument, required JSON> to json.Unmarshaller", 696 count: 2, 697 columns: []*column{{ 698 name: "jsondocument", 699 typeID: Ydb.Type_JSON_DOCUMENT, 700 optional: true, 701 }, { 702 name: "json", 703 typeID: Ydb.Type_JSON, 704 }}, 705 values: []indexed.RequiredOrOptional{ 706 new(jsonUnmarshaller), 707 new(jsonUnmarshaller), 708 }, 709 }, { 710 name: "<required JSONDocument, optional JSON> to json.Unmarshaller", 711 count: 2, 712 columns: []*column{{ 713 name: "jsondocument", 714 typeID: Ydb.Type_JSON_DOCUMENT, 715 }, { 716 name: "json", 717 typeID: Ydb.Type_JSON, 718 optional: true, 719 }}, 720 values: []indexed.RequiredOrOptional{ 721 new(jsonUnmarshaller), 722 new(jsonUnmarshaller), 723 }, 724 }, { 725 name: "<optional JSONDocument, optional JSON> to json.Unmarshaller", 726 count: 2, 727 columns: []*column{{ 728 name: "jsondocument", 729 typeID: Ydb.Type_JSON_DOCUMENT, 730 optional: true, 731 }, { 732 name: "json", 733 typeID: Ydb.Type_JSON, 734 optional: true, 735 }}, 736 values: []indexed.RequiredOrOptional{ 737 new(jsonUnmarshaller), 738 new(jsonUnmarshaller), 739 }, 740 }, { 741 name: "<required JSONDocument, required JSON> to json.Unmarshaller", 742 count: 2, 743 columns: []*column{{ 744 name: "jsondocument", 745 typeID: Ydb.Type_JSON_DOCUMENT, 746 }, { 747 name: "json", 748 typeID: Ydb.Type_JSON, 749 }}, 750 values: []indexed.RequiredOrOptional{ 751 new(jsonUnmarshaller), 752 new(jsonUnmarshaller), 753 }, 754 }, 755 } { 756 set, _ := getResultSet(test.count, test.columns) 757 s.reset(set) 758 for s.NextRow() { 759 values := make([]indexed.RequiredOrOptional, 0, len(test.values)) 760 for i, col := range test.columns { 761 if col.optional { 762 values = append( 763 values, 764 indexed.Optional( 765 test.values[i], 766 ), 767 ) 768 } else { 769 values = append( 770 values, 771 indexed.Required( 772 test.values[i], 773 ), 774 ) 775 } 776 } 777 if err := s.Scan(values...); err != nil { 778 t.Fatalf("test: %s; error: %s", test.name, err) 779 } 780 } 781 } 782 }