github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/query/scanner/named_test.go (about) 1 package scanner 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/require" 9 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 10 11 "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" 12 ) 13 14 func TestNamed(t *testing.T) { 15 for _, tt := range []struct { 16 name string 17 s NamedScanner 18 dst [][]interface{} 19 exp [][]interface{} 20 }{ 21 { 22 name: "Ydb.Type_UTF8", 23 s: Named(Data( 24 []*Ydb.Column{ 25 { 26 Name: "a", 27 Type: &Ydb.Type{ 28 Type: &Ydb.Type_TypeId{ 29 TypeId: Ydb.Type_UTF8, 30 }, 31 }, 32 }, 33 }, 34 []*Ydb.Value{ 35 { 36 Value: &Ydb.Value_TextValue{ 37 TextValue: "test", 38 }, 39 }, 40 }, 41 )), 42 dst: [][]interface{}{ 43 {func(v string) *string { return &v }("")}, 44 {func(v []byte) *[]byte { return &v }([]byte(""))}, 45 }, 46 exp: [][]interface{}{ 47 {func(v string) *string { return &v }("test")}, 48 {func(v []byte) *[]byte { return &v }([]byte("test"))}, 49 }, 50 }, 51 { 52 name: "Ydb.Type_STRING", 53 s: Named(Data( 54 []*Ydb.Column{ 55 { 56 Name: "a", 57 Type: &Ydb.Type{ 58 Type: &Ydb.Type_TypeId{ 59 TypeId: Ydb.Type_STRING, 60 }, 61 }, 62 }, 63 }, 64 []*Ydb.Value{ 65 { 66 Value: &Ydb.Value_BytesValue{ 67 BytesValue: []byte("test"), 68 }, 69 }, 70 }, 71 )), 72 dst: [][]interface{}{ 73 {func(v string) *string { return &v }("")}, 74 {func(v []byte) *[]byte { return &v }([]byte(""))}, 75 }, 76 exp: [][]interface{}{ 77 {func(v string) *string { return &v }("test")}, 78 {func(v []byte) *[]byte { return &v }([]byte("test"))}, 79 }, 80 }, 81 { 82 name: "Ydb.Type_UINT64", 83 s: Named(Data( 84 []*Ydb.Column{ 85 { 86 Name: "a", 87 Type: &Ydb.Type{ 88 Type: &Ydb.Type_TypeId{ 89 TypeId: Ydb.Type_UINT64, 90 }, 91 }, 92 }, 93 }, 94 []*Ydb.Value{ 95 { 96 Value: &Ydb.Value_Uint64Value{ 97 Uint64Value: 123, 98 }, 99 }, 100 }, 101 )), 102 dst: [][]interface{}{ 103 {func(v uint64) *uint64 { return &v }(0)}, 104 }, 105 exp: [][]interface{}{ 106 {func(v uint64) *uint64 { return &v }(123)}, 107 }, 108 }, 109 { 110 name: "Ydb.Type_INT64", 111 s: Named(Data( 112 []*Ydb.Column{ 113 { 114 Name: "a", 115 Type: &Ydb.Type{ 116 Type: &Ydb.Type_TypeId{ 117 TypeId: Ydb.Type_INT64, 118 }, 119 }, 120 }, 121 }, 122 []*Ydb.Value{ 123 { 124 Value: &Ydb.Value_Int64Value{ 125 Int64Value: 123, 126 }, 127 }, 128 }, 129 )), 130 dst: [][]interface{}{ 131 {func(v int64) *int64 { return &v }(0)}, 132 }, 133 exp: [][]interface{}{ 134 {func(v int64) *int64 { return &v }(123)}, 135 }, 136 }, 137 { 138 name: "Ydb.Type_UINT32", 139 s: Named(Data( 140 []*Ydb.Column{ 141 { 142 Name: "a", 143 Type: &Ydb.Type{ 144 Type: &Ydb.Type_TypeId{ 145 TypeId: Ydb.Type_UINT32, 146 }, 147 }, 148 }, 149 }, 150 []*Ydb.Value{ 151 { 152 Value: &Ydb.Value_Uint32Value{ 153 Uint32Value: 123, 154 }, 155 }, 156 }, 157 )), 158 dst: [][]interface{}{ 159 {func(v uint64) *uint64 { return &v }(0)}, 160 {func(v int64) *int64 { return &v }(0)}, 161 {func(v uint32) *uint32 { return &v }(0)}, 162 {func(v float64) *float64 { return &v }(0)}, 163 }, 164 exp: [][]interface{}{ 165 {func(v uint64) *uint64 { return &v }(123)}, 166 {func(v int64) *int64 { return &v }(123)}, 167 {func(v uint32) *uint32 { return &v }(123)}, 168 {func(v float64) *float64 { return &v }(123)}, 169 }, 170 }, 171 { 172 name: "Ydb.Type_INT32", 173 s: Named(Data( 174 []*Ydb.Column{ 175 { 176 Name: "a", 177 Type: &Ydb.Type{ 178 Type: &Ydb.Type_TypeId{ 179 TypeId: Ydb.Type_INT32, 180 }, 181 }, 182 }, 183 }, 184 []*Ydb.Value{ 185 { 186 Value: &Ydb.Value_Int32Value{ 187 Int32Value: 123, 188 }, 189 }, 190 }, 191 )), 192 dst: [][]interface{}{ 193 {func(v int64) *int64 { return &v }(0)}, 194 {func(v int32) *int32 { return &v }(0)}, 195 {func(v int) *int { return &v }(0)}, 196 {func(v float32) *float32 { return &v }(0)}, 197 {func(v float64) *float64 { return &v }(0)}, 198 }, 199 exp: [][]interface{}{ 200 {func(v int64) *int64 { return &v }(123)}, 201 {func(v int32) *int32 { return &v }(123)}, 202 {func(v int) *int { return &v }(123)}, 203 {func(v float32) *float32 { return &v }(123)}, 204 {func(v float64) *float64 { return &v }(123)}, 205 }, 206 }, 207 { 208 name: "Ydb.Type_UINT16", 209 s: Named(Data( 210 []*Ydb.Column{ 211 { 212 Name: "a", 213 Type: &Ydb.Type{ 214 Type: &Ydb.Type_TypeId{ 215 TypeId: Ydb.Type_UINT16, 216 }, 217 }, 218 }, 219 }, 220 []*Ydb.Value{ 221 { 222 Value: &Ydb.Value_Uint32Value{ 223 Uint32Value: 123, 224 }, 225 }, 226 }, 227 )), 228 dst: [][]interface{}{ 229 {func(v uint64) *uint64 { return &v }(0)}, 230 {func(v int64) *int64 { return &v }(0)}, 231 {func(v uint32) *uint32 { return &v }(0)}, 232 {func(v int32) *int32 { return &v }(0)}, 233 {func(v float32) *float32 { return &v }(0)}, 234 {func(v float64) *float64 { return &v }(0)}, 235 }, 236 exp: [][]interface{}{ 237 {func(v uint64) *uint64 { return &v }(123)}, 238 {func(v int64) *int64 { return &v }(123)}, 239 {func(v uint32) *uint32 { return &v }(123)}, 240 {func(v int32) *int32 { return &v }(123)}, 241 {func(v float32) *float32 { return &v }(123)}, 242 {func(v float64) *float64 { return &v }(123)}, 243 }, 244 }, 245 { 246 name: "Ydb.Type_INT16", 247 s: Named(Data( 248 []*Ydb.Column{ 249 { 250 Name: "a", 251 Type: &Ydb.Type{ 252 Type: &Ydb.Type_TypeId{ 253 TypeId: Ydb.Type_INT16, 254 }, 255 }, 256 }, 257 }, 258 []*Ydb.Value{ 259 { 260 Value: &Ydb.Value_Int32Value{ 261 Int32Value: 123, 262 }, 263 }, 264 }, 265 )), 266 dst: [][]interface{}{ 267 {func(v int64) *int64 { return &v }(0)}, 268 {func(v int32) *int32 { return &v }(0)}, 269 {func(v float32) *float32 { return &v }(0)}, 270 {func(v float64) *float64 { return &v }(0)}, 271 }, 272 exp: [][]interface{}{ 273 {func(v int64) *int64 { return &v }(123)}, 274 {func(v int32) *int32 { return &v }(123)}, 275 {func(v float32) *float32 { return &v }(123)}, 276 {func(v float64) *float64 { return &v }(123)}, 277 }, 278 }, 279 { 280 name: "Ydb.Type_UINT8", 281 s: Named(Data( 282 []*Ydb.Column{ 283 { 284 Name: "a", 285 Type: &Ydb.Type{ 286 Type: &Ydb.Type_TypeId{ 287 TypeId: Ydb.Type_UINT8, 288 }, 289 }, 290 }, 291 }, 292 []*Ydb.Value{ 293 { 294 Value: &Ydb.Value_Uint32Value{ 295 Uint32Value: 123, 296 }, 297 }, 298 }, 299 )), 300 dst: [][]interface{}{ 301 {func(v uint64) *uint64 { return &v }(0)}, 302 {func(v int64) *int64 { return &v }(0)}, 303 {func(v uint32) *uint32 { return &v }(0)}, 304 {func(v int32) *int32 { return &v }(0)}, 305 {func(v uint8) *uint8 { return &v }(0)}, 306 {func(v float32) *float32 { return &v }(0)}, 307 {func(v float64) *float64 { return &v }(0)}, 308 }, 309 exp: [][]interface{}{ 310 {func(v uint64) *uint64 { return &v }(123)}, 311 {func(v int64) *int64 { return &v }(123)}, 312 {func(v uint32) *uint32 { return &v }(123)}, 313 {func(v int32) *int32 { return &v }(123)}, 314 {func(v uint8) *uint8 { return &v }(123)}, 315 {func(v float32) *float32 { return &v }(123)}, 316 {func(v float64) *float64 { return &v }(123)}, 317 }, 318 }, 319 { 320 name: "Ydb.Type_INT8", 321 s: Named(Data( 322 []*Ydb.Column{ 323 { 324 Name: "a", 325 Type: &Ydb.Type{ 326 Type: &Ydb.Type_TypeId{ 327 TypeId: Ydb.Type_INT8, 328 }, 329 }, 330 }, 331 }, 332 []*Ydb.Value{ 333 { 334 Value: &Ydb.Value_Int32Value{ 335 Int32Value: 123, 336 }, 337 }, 338 }, 339 )), 340 dst: [][]interface{}{ 341 {func(v int64) *int64 { return &v }(0)}, 342 {func(v int32) *int32 { return &v }(0)}, 343 {func(v int8) *int8 { return &v }(0)}, 344 {func(v float32) *float32 { return &v }(0)}, 345 {func(v float64) *float64 { return &v }(0)}, 346 }, 347 exp: [][]interface{}{ 348 {func(v int64) *int64 { return &v }(123)}, 349 {func(v int32) *int32 { return &v }(123)}, 350 {func(v int8) *int8 { return &v }(123)}, 351 {func(v float32) *float32 { return &v }(123)}, 352 {func(v float64) *float64 { return &v }(123)}, 353 }, 354 }, 355 { 356 name: "Ydb.Type_BOOL", 357 s: Named(Data( 358 []*Ydb.Column{ 359 { 360 Name: "a", 361 Type: &Ydb.Type{ 362 Type: &Ydb.Type_TypeId{ 363 TypeId: Ydb.Type_BOOL, 364 }, 365 }, 366 }, 367 }, 368 []*Ydb.Value{ 369 { 370 Value: &Ydb.Value_BoolValue{ 371 BoolValue: true, 372 }, 373 }, 374 }, 375 )), 376 dst: [][]interface{}{ 377 {func(v bool) *bool { return &v }(false)}, 378 }, 379 exp: [][]interface{}{ 380 {func(v bool) *bool { return &v }(true)}, 381 }, 382 }, 383 { 384 name: "Ydb.Type_DATE", 385 s: Named(Data( 386 []*Ydb.Column{ 387 { 388 Name: "a", 389 Type: &Ydb.Type{ 390 Type: &Ydb.Type_TypeId{ 391 TypeId: Ydb.Type_DATE, 392 }, 393 }, 394 }, 395 }, 396 []*Ydb.Value{ 397 { 398 Value: &Ydb.Value_Uint32Value{ 399 Uint32Value: 100500, 400 }, 401 }, 402 }, 403 )), 404 dst: [][]interface{}{ 405 {func(v uint64) *uint64 { return &v }(0)}, 406 {func(v int64) *int64 { return &v }(0)}, 407 {func(v int32) *int32 { return &v }(0)}, 408 {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))}, 409 }, 410 exp: [][]interface{}{ 411 {func(v uint64) *uint64 { return &v }(100500)}, 412 {func(v int64) *int64 { return &v }(100500)}, 413 {func(v int32) *int32 { return &v }(100500)}, 414 {func(v time.Time) *time.Time { return &v }(time.Unix(8683200000, 0))}, 415 }, 416 }, 417 { 418 name: "Ydb.Type_DATETIME", 419 s: Named(Data( 420 []*Ydb.Column{ 421 { 422 Name: "a", 423 Type: &Ydb.Type{ 424 Type: &Ydb.Type_TypeId{ 425 TypeId: Ydb.Type_DATETIME, 426 }, 427 }, 428 }, 429 }, 430 []*Ydb.Value{ 431 { 432 Value: &Ydb.Value_Uint32Value{ 433 Uint32Value: 100500, 434 }, 435 }, 436 }, 437 )), 438 dst: [][]interface{}{ 439 {func(v uint64) *uint64 { return &v }(0)}, 440 {func(v int64) *int64 { return &v }(0)}, 441 {func(v uint32) *uint32 { return &v }(0)}, 442 {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))}, 443 }, 444 exp: [][]interface{}{ 445 {func(v uint64) *uint64 { return &v }(100500)}, 446 {func(v int64) *int64 { return &v }(100500)}, 447 {func(v uint32) *uint32 { return &v }(100500)}, 448 {func(v time.Time) *time.Time { return &v }(time.Unix(100500, 0))}, 449 }, 450 }, 451 { 452 name: "Ydb.Type_TIMESTAMP", 453 s: Named(Data( 454 []*Ydb.Column{ 455 { 456 Name: "a", 457 Type: &Ydb.Type{ 458 Type: &Ydb.Type_TypeId{ 459 TypeId: Ydb.Type_TIMESTAMP, 460 }, 461 }, 462 }, 463 }, 464 []*Ydb.Value{ 465 { 466 Value: &Ydb.Value_Uint64Value{ 467 Uint64Value: 12345678987654321, 468 }, 469 }, 470 }, 471 )), 472 dst: [][]interface{}{ 473 {func(v uint64) *uint64 { return &v }(0)}, 474 {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))}, 475 }, 476 exp: [][]interface{}{ 477 {func(v uint64) *uint64 { return &v }(12345678987654321)}, 478 {func(v time.Time) *time.Time { return &v }(time.Unix(12345678987, 654321000))}, 479 }, 480 }, 481 { 482 name: "Ydb.Type_INTERVAL", 483 s: Named(Data( 484 []*Ydb.Column{ 485 { 486 Name: "a", 487 Type: &Ydb.Type{ 488 Type: &Ydb.Type_TypeId{ 489 TypeId: Ydb.Type_INTERVAL, 490 }, 491 }, 492 }, 493 }, 494 []*Ydb.Value{ 495 { 496 Value: &Ydb.Value_Int64Value{ 497 Int64Value: 100500, 498 }, 499 }, 500 }, 501 )), 502 dst: [][]interface{}{ 503 {func(v int64) *int64 { return &v }(0)}, 504 {func(v time.Duration) *time.Duration { return &v }(time.Duration(0))}, 505 }, 506 exp: [][]interface{}{ 507 {func(v int64) *int64 { return &v }(100500)}, 508 {func(v time.Duration) *time.Duration { return &v }(time.Duration(100500000))}, 509 }, 510 }, 511 } { 512 for i := range tt.dst { 513 t.Run(tt.name+"→"+reflect.TypeOf(tt.dst[i][0]).Elem().String(), func(t *testing.T) { 514 err := tt.s.ScanNamed(func() (dst []NamedDestination) { 515 for j := range tt.dst[i] { 516 dst = append(dst, NamedRef("a", tt.dst[i][j])) 517 } 518 519 return dst 520 }()...) 521 require.NoError(t, err) 522 require.Equal(t, tt.exp[i], tt.dst[i]) 523 }) 524 } 525 } 526 } 527 528 func TestScannerNamedNotFoundByName(t *testing.T) { 529 scanner := Named(Data( 530 []*Ydb.Column{ 531 { 532 Name: "a", 533 Type: &Ydb.Type{ 534 Type: &Ydb.Type_TypeId{ 535 TypeId: Ydb.Type_UTF8, 536 }, 537 }, 538 }, 539 }, 540 []*Ydb.Value{ 541 { 542 Value: &Ydb.Value_TextValue{ 543 TextValue: "test", 544 }, 545 }, 546 }, 547 )) 548 var s string 549 err := scanner.ScanNamed(NamedRef("b", &s)) 550 require.ErrorIs(t, err, errColumnsNotFoundInRow) 551 } 552 553 func TestScannerNamedOrdering(t *testing.T) { 554 scanner := Named(Data( 555 []*Ydb.Column{ 556 { 557 Name: "a", 558 Type: &Ydb.Type{ 559 Type: &Ydb.Type_TypeId{ 560 TypeId: Ydb.Type_UTF8, 561 }, 562 }, 563 }, 564 { 565 Name: "b", 566 Type: &Ydb.Type{ 567 Type: &Ydb.Type_TypeId{ 568 TypeId: Ydb.Type_UTF8, 569 }, 570 }, 571 }, 572 { 573 Name: "c", 574 Type: &Ydb.Type{ 575 Type: &Ydb.Type_TypeId{ 576 TypeId: Ydb.Type_UTF8, 577 }, 578 }, 579 }, 580 }, 581 []*Ydb.Value{ 582 { 583 Value: &Ydb.Value_TextValue{ 584 TextValue: "A", 585 }, 586 }, 587 { 588 Value: &Ydb.Value_TextValue{ 589 TextValue: "B", 590 }, 591 }, 592 { 593 Value: &Ydb.Value_TextValue{ 594 TextValue: "C", 595 }, 596 }, 597 }, 598 )) 599 var a, b, c string 600 err := scanner.ScanNamed( 601 NamedRef("c", &c), 602 NamedRef("b", &b), 603 NamedRef("a", &a), 604 ) 605 require.NoError(t, err) 606 require.Equal(t, "A", a) 607 require.Equal(t, "B", b) 608 require.Equal(t, "C", c) 609 } 610 611 func TestNamedRef(t *testing.T) { 612 for _, tt := range []struct { 613 name string 614 ref interface{} 615 dst NamedDestination 616 panic bool 617 }{ 618 { 619 name: "", 620 ref: nil, 621 dst: NamedDestination{}, 622 panic: true, 623 }, 624 { 625 name: "nil_ref", 626 ref: nil, 627 dst: NamedDestination{}, 628 panic: true, 629 }, 630 { 631 name: "not_ref", 632 ref: 123, 633 dst: NamedDestination{}, 634 panic: true, 635 }, 636 { 637 name: "int_ptr", 638 ref: func(v int) *int { return &v }(123), 639 dst: NamedDestination{ 640 name: "int_ptr", 641 ref: func(v int) *int { return &v }(123), 642 }, 643 panic: false, 644 }, 645 { 646 name: "int_dbl_ptr", 647 ref: func(v int) **int { 648 vv := &v 649 650 return &vv 651 }(123), 652 dst: NamedDestination{ 653 name: "int_dbl_ptr", 654 ref: func(v int) **int { 655 vv := &v 656 657 return &vv 658 }(123), 659 }, 660 panic: false, 661 }, 662 } { 663 t.Run(tt.name, func(t *testing.T) { 664 if tt.panic { 665 defer func() { 666 require.NotNil(t, recover()) 667 }() 668 } else { 669 defer func() { 670 require.Nil(t, recover()) 671 }() 672 } 673 require.Equal(t, tt.dst, NamedRef(tt.name, tt.ref)) 674 }) 675 } 676 } 677 678 func TestNamedCastFailed(t *testing.T) { 679 scanner := Named(Data( 680 []*Ydb.Column{ 681 { 682 Name: "a", 683 Type: &Ydb.Type{ 684 Type: &Ydb.Type_TypeId{ 685 TypeId: Ydb.Type_UTF8, 686 }, 687 }, 688 }, 689 }, 690 []*Ydb.Value{ 691 { 692 Value: &Ydb.Value_TextValue{ 693 TextValue: "test", 694 }, 695 }, 696 }, 697 )) 698 var A uint64 699 err := scanner.ScanNamed(NamedRef("a", &A)) 700 require.ErrorIs(t, err, value.ErrCannotCast) 701 }