github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/value/value_test.go (about) 1 package value 2 3 import ( 4 "fmt" 5 "math" 6 "math/big" 7 "reflect" 8 "strconv" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/require" 13 "google.golang.org/protobuf/proto" 14 15 "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator" 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/types" 17 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" 18 ) 19 20 func BenchmarkMemory(b *testing.B) { 21 b.ReportAllocs() 22 v := TupleValue( 23 VoidValue(), 24 BoolValue(true), 25 Int8Value(1), 26 Int16Value(1), 27 Int32Value(1), 28 Int64Value(1), 29 Uint8Value(1), 30 Uint16Value(1), 31 Uint32Value(1), 32 Uint64Value(1), 33 DateValue(1), 34 DatetimeValue(1), 35 TimestampValue(1), 36 IntervalValue(1), 37 VoidValue(), 38 FloatValue(1), 39 DoubleValue(1), 40 BytesValue([]byte("test")), 41 DecimalValue([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, 22, 9), 42 DyNumberValue("123"), 43 JSONValue("{}"), 44 JSONDocumentValue("{}"), 45 TzDateValue("1"), 46 TzDatetimeValue("1"), 47 TzTimestampValue("1"), 48 TextValue("1"), 49 UUIDValue([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}), 50 YSONValue([]byte("{}")), 51 ListValue( 52 Int64Value(1), 53 Int64Value(2), 54 Int64Value(3), 55 ), 56 SetValue( 57 Int64Value(1), 58 Int64Value(2), 59 Int64Value(3), 60 ), 61 OptionalValue(IntervalValue(1)), 62 OptionalValue(OptionalValue(IntervalValue(1))), 63 StructValue( 64 StructValueField{"series_id", Uint64Value(1)}, 65 StructValueField{"title", TextValue("test")}, 66 StructValueField{"air_date", DateValue(1)}, 67 StructValueField{"remove_date", OptionalValue(TzDatetimeValue("1234"))}, 68 ), 69 DictValue( 70 DictValueField{TextValue("series_id"), Uint64Value(1)}, 71 DictValueField{TextValue("title"), Uint64Value(2)}, 72 DictValueField{TextValue("air_date"), Uint64Value(3)}, 73 DictValueField{TextValue("remove_date"), Uint64Value(4)}, 74 ), 75 NullValue(types.NewOptional(types.NewOptional(types.NewOptional(types.Bool)))), 76 VariantValueTuple(Int32Value(42), 1, types.NewTuple( 77 types.Bytes, 78 types.Int32, 79 )), 80 VariantValueStruct(Int32Value(42), "bar", types.NewStruct( 81 types.StructField{ 82 Name: "foo", 83 T: types.Bytes, 84 }, 85 types.StructField{ 86 Name: "bar", 87 T: types.Int32, 88 }, 89 )), 90 ZeroValue(types.Text), 91 ZeroValue(types.NewStruct()), 92 ZeroValue(types.NewTuple()), 93 ) 94 for i := 0; i < b.N; i++ { 95 a := allocator.New() 96 _ = ToYDB(v, a) 97 a.Free() 98 } 99 } 100 101 func TestToYDBFromYDB(t *testing.T) { 102 for i, v := range []Value{ 103 BoolValue(true), 104 Int8Value(1), 105 Int16Value(1), 106 Int32Value(1), 107 Int64Value(1), 108 Uint8Value(1), 109 Uint16Value(1), 110 Uint32Value(1), 111 Uint64Value(1), 112 DateValue(1), 113 DatetimeValue(1), 114 TimestampValue(1), 115 IntervalValue(1), 116 VoidValue(), 117 FloatValue(1), 118 DoubleValue(1), 119 BytesValue([]byte("test")), 120 DecimalValue([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, 22, 9), 121 DyNumberValue("123"), 122 JSONValue("{}"), 123 JSONDocumentValue("{}"), 124 TzDateValue("1"), 125 TzDatetimeValue("1"), 126 TzTimestampValue("1"), 127 TextValue("1"), 128 UUIDValue([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}), 129 YSONValue([]byte("{}")), 130 TupleValue( 131 Int64Value(1), 132 Int32Value(2), 133 Int16Value(3), 134 Int8Value(4), 135 ), 136 ListValue( 137 Int64Value(1), 138 Int64Value(2), 139 Int64Value(3), 140 ), 141 SetValue( 142 Int64Value(1), 143 Int64Value(2), 144 Int64Value(3), 145 ), 146 OptionalValue(IntervalValue(1)), 147 OptionalValue(OptionalValue(IntervalValue(1))), 148 StructValue( 149 StructValueField{"series_id", Uint64Value(1)}, 150 StructValueField{"title", TextValue("test")}, 151 StructValueField{"air_date", DateValue(1)}, 152 StructValueField{"remove_date", OptionalValue(TzDatetimeValue("1234"))}, 153 ), 154 DictValue( 155 DictValueField{TextValue("series_id"), Uint64Value(1)}, 156 DictValueField{TextValue("title"), Uint64Value(2)}, 157 DictValueField{TextValue("air_date"), Uint64Value(3)}, 158 DictValueField{TextValue("remove_date"), Uint64Value(4)}, 159 ), 160 NullValue(types.Bool), 161 NullValue(types.NewOptional(types.Bool)), 162 VariantValueTuple(Int32Value(42), 1, types.NewTuple( 163 types.Bytes, 164 types.Int32, 165 )), 166 VariantValueStruct(Int32Value(42), "bar", types.NewStruct( 167 types.StructField{ 168 Name: "foo", 169 T: types.Bytes, 170 }, 171 types.StructField{ 172 Name: "bar", 173 T: types.Int32, 174 }, 175 )), 176 ZeroValue(types.Text), 177 ZeroValue(types.NewStruct()), 178 ZeroValue(types.NewTuple()), 179 } { 180 t.Run(strconv.Itoa(i)+"."+v.Yql(), func(t *testing.T) { 181 a := allocator.New() 182 defer a.Free() 183 value := ToYDB(v, a) 184 dualConversedValue, err := fromYDB(value.GetType(), value.GetValue()) 185 require.NoError(t, err) 186 if !proto.Equal(value, ToYDB(dualConversedValue, a)) { 187 t.Errorf("dual conversion failed:\n\n - got: %v\n\n - want: %v", ToYDB(dualConversedValue, a), value) 188 } 189 }) 190 } 191 } 192 193 func TestValueYql(t *testing.T) { 194 for i, tt := range []struct { 195 value Value 196 literal string 197 }{ 198 { 199 value: VoidValue(), 200 literal: `Void()`, 201 }, 202 { 203 value: TextValue("some\"text\"with brackets"), 204 literal: `"some\"text\"with brackets"u`, 205 }, 206 { 207 value: TextValue(`some text with slashes \ \\ \\\`), 208 literal: `"some text with slashes \\ \\\\ \\\\\\"u`, 209 }, 210 { 211 value: BytesValue([]byte("foo")), 212 literal: `"foo"`, 213 }, 214 { 215 value: BytesValue([]byte("\xFE\xFF")), 216 literal: `"\xfe\xff"`, 217 }, 218 { 219 value: OptionalValue(BytesValue([]byte{0, 1, 2, 3, 4, 5, 6})), 220 literal: `Just("\x00\x01\x02\x03\x04\x05\x06")`, 221 }, 222 { 223 value: BoolValue(true), 224 literal: `true`, 225 }, 226 { 227 value: Int8Value(42), 228 literal: `42t`, 229 }, 230 { 231 value: Uint8Value(42), 232 literal: `42ut`, 233 }, 234 { 235 value: Int16Value(42), 236 literal: `42s`, 237 }, 238 { 239 value: Uint16Value(42), 240 literal: `42us`, 241 }, 242 { 243 value: Int32Value(42), 244 literal: `42`, 245 }, 246 { 247 value: Uint32Value(42), 248 literal: `42u`, 249 }, 250 { 251 value: Int64Value(42), 252 literal: `42l`, 253 }, 254 { 255 value: Uint64Value(42), 256 literal: `42ul`, 257 }, 258 { 259 value: Uint64Value(200000000000), 260 literal: `200000000000ul`, 261 }, 262 { 263 value: FloatValue(42.2121236), 264 literal: `Float("42.212124")`, 265 }, 266 { 267 value: FloatValue(float32(math.Inf(+1))), 268 literal: `Float("+Inf")`, 269 }, 270 { 271 value: FloatValue(float32(math.Inf(-1))), 272 literal: `Float("-Inf")`, 273 }, 274 { 275 value: FloatValue(float32(math.NaN())), 276 literal: `Float("NaN")`, 277 }, 278 { 279 value: DoubleValue(42.2121236192), 280 literal: `Double("42.2121236192")`, 281 }, 282 { 283 value: DoubleValue(math.Inf(+1)), 284 literal: `Double("+Inf")`, 285 }, 286 { 287 value: DoubleValue(math.Inf(-1)), 288 literal: `Double("-Inf")`, 289 }, 290 { 291 value: DoubleValue(math.NaN()), 292 literal: `Double("NaN")`, 293 }, 294 { 295 value: DateValue(func() uint32 { 296 v, _ := time.Parse("2006-01-02", "2022-06-17") 297 298 return uint32(v.Sub(time.Unix(0, 0)) / time.Hour / 24) 299 }()), 300 literal: `Date("2022-06-17")`, 301 }, 302 { 303 value: DatetimeValue(func() uint32 { 304 v, _ := time.Parse("2006-01-02 15:04:05", "2022-06-17 05:19:20") 305 306 return uint32(v.UTC().Sub(time.Unix(0, 0)).Seconds()) 307 }()), 308 literal: `Datetime("2022-06-17T05:19:20Z")`, 309 }, 310 { 311 value: TzDateValue("2022-06-17,Europe/Berlin"), 312 literal: `TzDate("2022-06-17,Europe/Berlin")`, 313 }, 314 { 315 value: TzDatetimeValue("2022-06-17T05:19:20,Europe/Berlin"), 316 literal: `TzDatetime("2022-06-17T05:19:20,Europe/Berlin")`, 317 }, 318 { 319 value: IntervalValueFromDuration(time.Duration(42) * time.Millisecond), 320 literal: `Interval("PT0.042000S")`, 321 }, 322 { 323 value: TimestampValueFromTime(func() time.Time { 324 tt, err := time.Parse(LayoutTimestamp, "1997-12-14T03:09:42.123456Z") 325 require.NoError(t, err) 326 327 return tt.UTC() 328 }()), 329 literal: `Timestamp("1997-12-14T03:09:42.123456Z")`, 330 }, 331 { 332 value: TzTimestampValue("1997-12-14T03:09:42.123456,Europe/Berlin"), 333 literal: `TzTimestamp("1997-12-14T03:09:42.123456,Europe/Berlin")`, 334 }, 335 { 336 value: NullValue(types.Int32), 337 literal: `Nothing(Optional<Int32>)`, 338 }, 339 { 340 value: NullValue(types.NewOptional(types.Bool)), 341 literal: `Nothing(Optional<Optional<Bool>>)`, 342 }, 343 { 344 value: Int32Value(42), 345 literal: `42`, 346 }, 347 { 348 value: OptionalValue(Int32Value(42)), 349 literal: `Just(42)`, 350 }, 351 { 352 value: OptionalValue(OptionalValue(Int32Value(42))), 353 literal: `Just(Just(42))`, 354 }, 355 { 356 value: OptionalValue(OptionalValue(OptionalValue(Int32Value(42)))), 357 literal: `Just(Just(Just(42)))`, 358 }, 359 { 360 value: ListValue( 361 Int32Value(-1), 362 Int32Value(0), 363 Int32Value(1), 364 Int32Value(2), 365 Int32Value(3), 366 ), 367 literal: `[-1,0,1,2,3]`, 368 }, 369 { 370 value: ListValue( 371 Int64Value(0), 372 Int64Value(1), 373 Int64Value(2), 374 Int64Value(3), 375 ), 376 literal: `[0l,1l,2l,3l]`, 377 }, 378 { 379 value: SetValue( 380 Int64Value(0), 381 Int64Value(1), 382 Int64Value(2), 383 Int64Value(3), 384 ), 385 literal: `{0l,1l,2l,3l}`, 386 }, 387 { 388 value: TupleValue( 389 Int32Value(0), 390 Int64Value(1), 391 FloatValue(2), 392 TextValue("3"), 393 ), 394 literal: `(0,1l,Float("2"),"3"u)`, 395 }, 396 { 397 value: VariantValueTuple(Int32Value(42), 1, types.NewTuple( 398 types.Bytes, 399 types.Int32, 400 )), 401 literal: `Variant(42,"1",Variant<String,Int32>)`, 402 }, 403 { 404 value: VariantValueTuple(TextValue("foo"), 1, types.NewTuple( 405 types.Bytes, 406 types.Text, 407 )), 408 literal: `Variant("foo"u,"1",Variant<String,Utf8>)`, 409 }, 410 { 411 value: VariantValueTuple(BoolValue(true), 0, types.NewTuple( 412 types.Bytes, 413 types.Int32, 414 )), 415 literal: `Variant(true,"0",Variant<String,Int32>)`, 416 }, 417 { 418 value: VariantValueStruct(Int32Value(42), "bar", types.NewStruct( 419 types.StructField{ 420 Name: "foo", 421 T: types.Bytes, 422 }, 423 types.StructField{ 424 Name: "bar", 425 T: types.Int32, 426 }, 427 )), 428 literal: `Variant(42,"bar",Variant<'bar':Int32,'foo':String>)`, 429 }, 430 { 431 value: StructValue( 432 StructValueField{"series_id", Uint64Value(1)}, 433 StructValueField{"title", TextValue("test")}, 434 StructValueField{"air_date", DateValue(1)}, 435 ), 436 literal: "<|`air_date`:Date(\"1970-01-02\"),`series_id`:1ul,`title`:\"test\"u|>", 437 }, 438 { 439 value: DictValue( 440 DictValueField{TextValue("foo"), Int32Value(42)}, 441 DictValueField{TextValue("bar"), Int32Value(43)}, 442 ), 443 literal: `{"bar"u:43,"foo"u:42}`, 444 }, 445 { 446 value: DictValue( 447 DictValueField{TextValue("foo"), VoidValue()}, 448 DictValueField{TextValue("bar"), VoidValue()}, 449 ), 450 literal: `{"bar"u:Void(),"foo"u:Void()}`, 451 }, 452 { 453 value: ZeroValue(types.Bool), 454 literal: `false`, 455 }, 456 { 457 value: ZeroValue(types.NewOptional(types.Bool)), 458 literal: `Nothing(Optional<Bool>)`, 459 }, 460 { 461 value: ZeroValue(types.NewTuple(types.Bool, types.Double)), 462 literal: `(false,Double("0"))`, 463 }, 464 { 465 value: ZeroValue(types.NewStruct( 466 types.StructField{ 467 Name: "foo", 468 T: types.Bool, 469 }, 470 types.StructField{ 471 Name: "bar", 472 T: types.Text, 473 }, 474 )), 475 literal: "<|`bar`:\"\"u,`foo`:false|>", 476 }, 477 { 478 value: ZeroValue(types.UUID), 479 literal: `Uuid("00000000-0000-0000-0000-000000000000")`, 480 }, 481 { 482 value: DecimalValueFromBigInt(big.NewInt(-1234567890123456), 22, 9), 483 literal: `Decimal("-1234567.890123456",22,9)`, 484 }, 485 { 486 value: DyNumberValue("-1234567890123456"), 487 literal: `DyNumber("-1234567890123456")`, 488 }, 489 { 490 value: JSONValue("{\"a\":-1234567890123456}"), 491 literal: `Json(@@{"a":-1234567890123456}@@)`, 492 }, 493 { 494 value: JSONDocumentValue("{\"a\":-1234567890123456}"), 495 literal: `JsonDocument(@@{"a":-1234567890123456}@@)`, 496 }, 497 { 498 value: YSONValue([]byte("<a=1>[3;%false]")), 499 literal: `Yson("<a=1>[3;%false]")`, 500 }, 501 } { 502 t.Run(strconv.Itoa(i)+"."+tt.literal, func(t *testing.T) { 503 require.Equal(t, tt.literal, tt.value.Yql()) 504 }) 505 } 506 } 507 508 func TestOptionalValueCastTo(t *testing.T) { 509 for _, tt := range []struct { 510 name string 511 v *optionalValue 512 dst **string 513 exp interface{} 514 err error 515 }{ 516 { 517 name: xtest.CurrentFileLine(), 518 v: OptionalValue(TextValue("test")), 519 dst: func(v *string) **string { return &v }(func(s string) *string { return &s }("")), 520 exp: func(v *string) **string { return &v }(func(s string) *string { return &s }("test")), 521 err: nil, 522 }, 523 { 524 name: xtest.CurrentFileLine(), 525 v: OptionalValue(TextValue("test")), 526 dst: func(v *string) **string { return &v }(func() *string { return nil }()), 527 exp: func(v *string) **string { return &v }(func(s string) *string { return &s }("test")), 528 err: nil, 529 }, 530 { 531 name: xtest.CurrentFileLine(), 532 v: NullValue(types.Text), 533 dst: func(v *string) **string { return &v }(func(s string) *string { return &s }("")), 534 exp: func(v *string) **string { return &v }(func() *string { return nil }()), 535 err: nil, 536 }, 537 { 538 name: xtest.CurrentFileLine(), 539 v: NullValue(types.Text), 540 dst: func(v *string) **string { return &v }(func() *string { return nil }()), 541 exp: func(v *string) **string { return &v }(func() *string { return nil }()), 542 err: nil, 543 }, 544 } { 545 t.Run(tt.name, func(t *testing.T) { 546 err := tt.v.castTo(tt.dst) 547 if tt.err != nil { 548 require.ErrorIs(t, err, tt.err) 549 } else { 550 require.NoError(t, err) 551 require.Equal(t, tt.exp, tt.dst) 552 } 553 }) 554 } 555 } 556 557 func TestNullable(t *testing.T) { 558 for _, test := range []struct { 559 name string 560 t types.Type 561 v interface{} 562 exp Value 563 }{ 564 { 565 name: "bool", 566 t: types.Bool, 567 v: func(v bool) *bool { return &v }(true), 568 exp: OptionalValue(BoolValue(true)), 569 }, 570 { 571 name: "nil bool", 572 t: types.Bool, 573 v: func() *bool { return nil }(), 574 exp: NullValue(types.Bool), 575 }, 576 { 577 name: "int8", 578 t: types.Int8, 579 v: func(v int8) *int8 { return &v }(123), 580 exp: OptionalValue(Int8Value(123)), 581 }, 582 { 583 name: "nil int8", 584 t: types.Int8, 585 v: func() *int8 { return nil }(), 586 exp: NullValue(types.Int8), 587 }, 588 { 589 name: "uint8", 590 t: types.Uint8, 591 v: func(v uint8) *uint8 { return &v }(123), 592 exp: OptionalValue(Uint8Value(123)), 593 }, 594 { 595 name: "nil uint8", 596 t: types.Uint8, 597 v: func() *uint8 { return nil }(), 598 exp: NullValue(types.Uint8), 599 }, 600 { 601 name: "int16", 602 t: types.Int16, 603 v: func(v int16) *int16 { return &v }(123), 604 exp: OptionalValue(Int16Value(123)), 605 }, 606 { 607 name: "nil int16", 608 t: types.Int16, 609 v: func() *int16 { return nil }(), 610 exp: NullValue(types.Int16), 611 }, 612 { 613 name: "uint16", 614 t: types.Uint16, 615 v: func(v uint16) *uint16 { return &v }(123), 616 exp: OptionalValue(Uint16Value(123)), 617 }, 618 { 619 name: "nil uint16", 620 t: types.Uint16, 621 v: func() *uint16 { return nil }(), 622 exp: NullValue(types.Uint16), 623 }, 624 { 625 name: "int32", 626 t: types.Int32, 627 v: func(v int32) *int32 { return &v }(123), 628 exp: OptionalValue(Int32Value(123)), 629 }, 630 { 631 name: "nil int32", 632 t: types.Int32, 633 v: func() *int32 { return nil }(), 634 exp: NullValue(types.Int32), 635 }, 636 { 637 name: "uint32", 638 t: types.Uint32, 639 v: func(v uint32) *uint32 { return &v }(123), 640 exp: OptionalValue(Uint32Value(123)), 641 }, 642 { 643 name: "nil uint32", 644 t: types.Uint32, 645 v: func() *uint32 { return nil }(), 646 exp: NullValue(types.Uint32), 647 }, 648 { 649 name: "int64", 650 t: types.Int64, 651 v: func(v int64) *int64 { return &v }(123), 652 exp: OptionalValue(Int64Value(123)), 653 }, 654 { 655 name: "nil int64", 656 t: types.Int64, 657 v: func() *int64 { return nil }(), 658 exp: NullValue(types.Int64), 659 }, 660 { 661 name: "uint64", 662 t: types.Uint64, 663 v: func(v uint64) *uint64 { return &v }(123), 664 exp: OptionalValue(Uint64Value(123)), 665 }, 666 { 667 name: "nil uint64", 668 t: types.Uint64, 669 v: func() *uint64 { return nil }(), 670 exp: NullValue(types.Uint64), 671 }, 672 { 673 name: "float", 674 t: types.Float, 675 v: func(v float32) *float32 { return &v }(123), 676 exp: OptionalValue(FloatValue(123)), 677 }, 678 { 679 name: "nil float", 680 t: types.Float, 681 v: func() *float32 { return nil }(), 682 exp: NullValue(types.Float), 683 }, 684 { 685 name: "double", 686 t: types.Double, 687 v: func(v float64) *float64 { return &v }(123), 688 exp: OptionalValue(DoubleValue(123)), 689 }, 690 { 691 name: "nil float", 692 t: types.Double, 693 v: func() *float64 { return nil }(), 694 exp: NullValue(types.Double), 695 }, 696 { 697 name: "date from int32", 698 t: types.Date, 699 v: func(v uint32) *uint32 { return &v }(123), 700 exp: OptionalValue(DateValue(123)), 701 }, 702 { 703 name: "date from time.Time", 704 t: types.Date, 705 v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)), 706 exp: OptionalValue(DateValueFromTime(time.Unix(123, 456))), 707 }, 708 { 709 name: "nil date", 710 t: types.Date, 711 v: func() *uint32 { return nil }(), 712 exp: NullValue(types.Date), 713 }, 714 { 715 name: "datetime from int32", 716 t: types.Datetime, 717 v: func(v uint32) *uint32 { return &v }(123), 718 exp: OptionalValue(DatetimeValue(123)), 719 }, 720 { 721 name: "datetime from time.Time", 722 t: types.Datetime, 723 v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)), 724 exp: OptionalValue(DatetimeValueFromTime(time.Unix(123, 456))), 725 }, 726 { 727 name: "nil datetime", 728 t: types.Datetime, 729 v: func() *uint32 { return nil }(), 730 exp: NullValue(types.Datetime), 731 }, 732 { 733 name: "timestamp from int32", 734 t: types.Timestamp, 735 v: func(v uint64) *uint64 { return &v }(123), 736 exp: OptionalValue(TimestampValue(123)), 737 }, 738 { 739 name: "timestamp from time.Time", 740 t: types.Timestamp, 741 v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)), 742 exp: OptionalValue(TimestampValueFromTime(time.Unix(123, 456))), 743 }, 744 { 745 name: "nil timestamp", 746 t: types.Timestamp, 747 v: func() *uint64 { return nil }(), 748 exp: NullValue(types.Timestamp), 749 }, 750 { 751 name: "tzDate from int32", 752 t: types.TzDate, 753 v: func(v string) *string { return &v }(""), 754 exp: OptionalValue(TzDateValue("")), 755 }, 756 { 757 name: "tzDate from time.Time", 758 t: types.TzDate, 759 v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)), 760 exp: OptionalValue(TzDateValueFromTime(time.Unix(123, 456))), 761 }, 762 { 763 name: "nil tzDate", 764 t: types.TzDate, 765 v: func() *string { return nil }(), 766 exp: NullValue(types.TzDate), 767 }, 768 { 769 name: "interval from int64", 770 t: types.Interval, 771 v: func(v int64) *int64 { return &v }(123), 772 exp: OptionalValue(IntervalValue(123)), 773 }, 774 { 775 name: "interval from time.Time", 776 t: types.Interval, 777 v: func(v time.Duration) *time.Duration { return &v }(time.Second), 778 exp: OptionalValue(IntervalValueFromDuration(time.Second)), 779 }, 780 { 781 name: "nil interval", 782 t: types.Interval, 783 v: func() *int64 { return nil }(), 784 exp: NullValue(types.Interval), 785 }, 786 { 787 name: "tzDatetime from int32", 788 t: types.TzDatetime, 789 v: func(v string) *string { return &v }(""), 790 exp: OptionalValue(TzDatetimeValue("")), 791 }, 792 { 793 name: "tzTzDatetime from time.Time", 794 t: types.TzDatetime, 795 v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)), 796 exp: OptionalValue(TzDatetimeValueFromTime(time.Unix(123, 456))), 797 }, 798 { 799 name: "nil tzTzDatetime", 800 t: types.TzDatetime, 801 v: func() *string { return nil }(), 802 exp: NullValue(types.TzDatetime), 803 }, 804 { 805 name: "tzTimestamp from int32", 806 t: types.TzTimestamp, 807 v: func(v string) *string { return &v }(""), 808 exp: OptionalValue(TzTimestampValue("")), 809 }, 810 { 811 name: "TzTimestamp from time.Time", 812 t: types.TzTimestamp, 813 v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)), 814 exp: OptionalValue(TzTimestampValueFromTime(time.Unix(123, 456))), 815 }, 816 { 817 name: "nil TzTimestamp", 818 t: types.TzTimestamp, 819 v: func() *string { return nil }(), 820 exp: NullValue(types.TzTimestamp), 821 }, 822 { 823 name: "string", 824 t: types.Bytes, 825 v: func(v string) *string { return &v }("test"), 826 exp: OptionalValue(BytesValue([]byte("test"))), 827 }, 828 { 829 name: "string", 830 t: types.Bytes, 831 v: func(v []byte) *[]byte { return &v }([]byte("test")), 832 exp: OptionalValue(BytesValue([]byte("test"))), 833 }, 834 { 835 name: "nil string", 836 t: types.Bytes, 837 v: func() *string { return nil }(), 838 exp: NullValue(types.Bytes), 839 }, 840 { 841 name: "utf8", 842 t: types.Text, 843 v: func(v string) *string { return &v }("test"), 844 exp: OptionalValue(TextValue("test")), 845 }, 846 { 847 name: "nil utf8", 848 t: types.Text, 849 v: func() *string { return nil }(), 850 exp: NullValue(types.Text), 851 }, 852 { 853 name: "yson", 854 t: types.YSON, 855 v: func(v string) *string { return &v }("test"), 856 exp: OptionalValue(YSONValue([]byte("test"))), 857 }, 858 { 859 name: "yson", 860 t: types.YSON, 861 v: func(v []byte) *[]byte { return &v }([]byte("test")), 862 exp: OptionalValue(YSONValue([]byte("test"))), 863 }, 864 { 865 name: "nil yson", 866 t: types.YSON, 867 v: func() *string { return nil }(), 868 exp: NullValue(types.YSON), 869 }, 870 { 871 name: "json", 872 t: types.JSON, 873 v: func(v string) *string { return &v }("test"), 874 exp: OptionalValue(JSONValue("test")), 875 }, 876 { 877 name: "json", 878 t: types.JSON, 879 v: func(v []byte) *[]byte { return &v }([]byte("test")), 880 exp: OptionalValue(JSONValue("test")), 881 }, 882 { 883 name: "nil json", 884 t: types.JSON, 885 v: func() *string { return nil }(), 886 exp: NullValue(types.JSON), 887 }, 888 { 889 name: "uuid", 890 t: types.UUID, 891 v: func(v [16]byte) *[16]byte { return &v }([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), 892 exp: OptionalValue(UUIDValue([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})), 893 }, 894 { 895 name: "jsonDocument", 896 t: types.JSONDocument, 897 v: func(v string) *string { return &v }("test"), 898 exp: OptionalValue(JSONDocumentValue("test")), 899 }, 900 { 901 name: "jsonDocument", 902 t: types.JSONDocument, 903 v: func(v []byte) *[]byte { return &v }([]byte("test")), 904 exp: OptionalValue(JSONDocumentValue("test")), 905 }, 906 { 907 name: "nil jsonDocument", 908 t: types.JSONDocument, 909 v: func() *string { return nil }(), 910 exp: NullValue(types.JSONDocument), 911 }, 912 { 913 name: "dyNumber", 914 t: types.DyNumber, 915 v: func(v string) *string { return &v }("test"), 916 exp: OptionalValue(DyNumberValue("test")), 917 }, 918 { 919 name: "nil dyNumber", 920 t: types.DyNumber, 921 v: func() *string { return nil }(), 922 exp: NullValue(types.DyNumber), 923 }, 924 } { 925 t.Run(test.name, func(t *testing.T) { 926 a := allocator.New() 927 defer a.Free() 928 v := Nullable(test.t, test.v) 929 if !proto.Equal(ToYDB(v, a), ToYDB(test.exp, a)) { 930 t.Fatalf("unexpected value: %v, exp: %v", v, test.exp) 931 } 932 }) 933 } 934 } 935 936 func TestCastNumbers(t *testing.T) { 937 numberValues := []struct { 938 value Value 939 signed bool 940 len int 941 }{ 942 { 943 value: Uint64Value(1), 944 signed: false, 945 len: 8, 946 }, 947 { 948 value: Int64Value(2), 949 signed: true, 950 len: 8, 951 }, 952 { 953 value: Uint32Value(3), 954 signed: false, 955 len: 4, 956 }, 957 { 958 value: Int32Value(4), 959 signed: true, 960 len: 4, 961 }, 962 { 963 value: Uint16Value(5), 964 signed: false, 965 len: 2, 966 }, 967 { 968 value: Int16Value(6), 969 signed: true, 970 len: 2, 971 }, 972 { 973 value: Uint8Value(7), 974 signed: false, 975 len: 1, 976 }, 977 { 978 value: Int8Value(8), 979 signed: true, 980 len: 1, 981 }, 982 } 983 numberDestinations := []struct { 984 destination interface{} 985 signed bool 986 len int 987 }{ 988 { 989 destination: func(v uint64) *uint64 { return &v }(1), 990 signed: false, 991 len: 8, 992 }, 993 { 994 destination: func(v int64) *int64 { return &v }(2), 995 signed: true, 996 len: 8, 997 }, 998 { 999 destination: func(v uint32) *uint32 { return &v }(3), 1000 signed: false, 1001 len: 4, 1002 }, 1003 { 1004 destination: func(v int32) *int32 { return &v }(4), 1005 signed: true, 1006 len: 4, 1007 }, 1008 { 1009 destination: func(v uint16) *uint16 { return &v }(5), 1010 signed: false, 1011 len: 2, 1012 }, 1013 { 1014 destination: func(v int16) *int16 { return &v }(6), 1015 signed: true, 1016 len: 2, 1017 }, 1018 { 1019 destination: func(v uint8) *uint8 { return &v }(7), 1020 signed: false, 1021 len: 1, 1022 }, 1023 { 1024 destination: func(v int8) *int8 { return &v }(8), 1025 signed: true, 1026 len: 1, 1027 }, 1028 { 1029 destination: func(v float32) *float32 { return &v }(7), 1030 signed: true, 1031 len: 4, 1032 }, 1033 { 1034 destination: func(v float64) *float64 { return &v }(8), 1035 signed: true, 1036 len: 8, 1037 }, 1038 } 1039 for _, dst := range numberDestinations { 1040 for _, src := range numberValues { 1041 t.Run(fmt.Sprintf("%sā%s", 1042 src.value.Yql(), reflect.ValueOf(dst.destination).Type().Elem().String(), 1043 ), func(t *testing.T) { 1044 mustErr := false 1045 switch { 1046 case src.len == dst.len && src.signed != dst.signed, 1047 src.len > dst.len, 1048 src.signed && !dst.signed: 1049 mustErr = true 1050 } 1051 err := CastTo(src.value, dst.destination) 1052 if mustErr { 1053 require.Error(t, err) 1054 } else { 1055 require.NoError(t, err) 1056 } 1057 }) 1058 t.Run(fmt.Sprintf("Optional(%s)ā%s", 1059 src.value.Yql(), reflect.ValueOf(dst.destination).Type().Elem().String(), 1060 ), func(t *testing.T) { 1061 mustErr := false 1062 switch { 1063 case src.len == dst.len && src.signed != dst.signed, 1064 src.len > dst.len, 1065 src.signed && !dst.signed: 1066 mustErr = true 1067 } 1068 err := CastTo(OptionalValue(src.value), dst.destination) 1069 if mustErr { 1070 require.Error(t, err) 1071 } else { 1072 require.NoError(t, err) 1073 } 1074 }) 1075 } 1076 } 1077 } 1078 1079 func TestCastOtherTypes(t *testing.T) { 1080 for _, tt := range []struct { 1081 v Value 1082 dst interface{} 1083 result interface{} 1084 error bool 1085 }{ 1086 { 1087 v: BytesValue([]byte("test")), 1088 dst: func(v []byte) *[]byte { return &v }(make([]byte, 0, 10)), 1089 result: func(v []byte) *[]byte { return &v }([]byte("test")), 1090 error: false, 1091 }, 1092 { 1093 v: TextValue("test"), 1094 dst: func(v []byte) *[]byte { return &v }(make([]byte, 0, 10)), 1095 result: func(v []byte) *[]byte { return &v }([]byte("test")), 1096 error: false, 1097 }, 1098 { 1099 v: BytesValue([]byte("test")), 1100 dst: func(v string) *string { return &v }(""), 1101 result: func(v string) *string { return &v }("test"), 1102 error: false, 1103 }, 1104 { 1105 v: DoubleValue(123), 1106 dst: func(v float64) *float64 { return &v }(9), 1107 result: func(v float64) *float64 { return &v }(123), 1108 error: false, 1109 }, 1110 { 1111 v: DoubleValue(123), 1112 dst: func(v float32) *float32 { return &v }(9), 1113 result: func(v float32) *float32 { return &v }(9), 1114 error: true, 1115 }, 1116 { 1117 v: FloatValue(123), 1118 dst: func(v float64) *float64 { return &v }(9), 1119 result: func(v float64) *float64 { return &v }(123), 1120 error: false, 1121 }, 1122 { 1123 v: FloatValue(123), 1124 dst: func(v float32) *float32 { return &v }(9), 1125 result: func(v float32) *float32 { return &v }(123), 1126 error: false, 1127 }, 1128 { 1129 v: Uint64Value(123), 1130 dst: func(v float32) *float32 { return &v }(9), 1131 result: func(v float32) *float32 { return &v }(9), 1132 error: true, 1133 }, 1134 { 1135 v: Uint64Value(123), 1136 dst: func(v float64) *float64 { return &v }(9), 1137 result: func(v float64) *float64 { return &v }(9), 1138 error: true, 1139 }, 1140 { 1141 v: OptionalValue(DoubleValue(123)), 1142 dst: func(v float64) *float64 { return &v }(9), 1143 result: func(v float64) *float64 { return &v }(123), 1144 error: false, 1145 }, 1146 } { 1147 t.Run(fmt.Sprintf("%sā%v", tt.v.Type().Yql(), reflect.ValueOf(tt.dst).Type().Elem()), 1148 func(t *testing.T) { 1149 if err := CastTo(tt.v, tt.dst); (err != nil) != tt.error { 1150 t.Errorf("castTo() error = %v, want %v", err, tt.error) 1151 } else if !reflect.DeepEqual(tt.dst, tt.result) { 1152 t.Errorf("castTo() result = %+v, want %+v", 1153 reflect.ValueOf(tt.dst).Elem(), 1154 reflect.ValueOf(tt.result).Elem(), 1155 ) 1156 } 1157 }, 1158 ) 1159 } 1160 }