github.com/snowflakedb/gosnowflake@v1.9.0/converter_test.go (about) 1 // Copyright (c) 2017-2022 Snowflake Computing Inc. All rights reserved. 2 3 package gosnowflake 4 5 import ( 6 "context" 7 "database/sql" 8 "database/sql/driver" 9 "fmt" 10 "io" 11 "math" 12 "math/big" 13 "math/cmplx" 14 "reflect" 15 "strconv" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/apache/arrow/go/v15/arrow" 21 "github.com/apache/arrow/go/v15/arrow/array" 22 "github.com/apache/arrow/go/v15/arrow/decimal128" 23 "github.com/apache/arrow/go/v15/arrow/memory" 24 ) 25 26 func stringIntToDecimal(src string) (decimal128.Num, bool) { 27 b, ok := new(big.Int).SetString(src, 10) 28 if !ok { 29 return decimal128.Num{}, ok 30 } 31 var high, low big.Int 32 high.QuoRem(b, decimalShift, &low) 33 return decimal128.New(high.Int64(), low.Uint64()), ok 34 } 35 36 func stringFloatToDecimal(src string, scale int64) (decimal128.Num, bool) { 37 b, ok := new(big.Float).SetString(src) 38 if !ok { 39 return decimal128.Num{}, ok 40 } 41 s := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(scale), nil)) 42 n := new(big.Float).Mul(b, s) 43 if !n.IsInt() { 44 return decimal128.Num{}, false 45 } 46 var high, low, z big.Int 47 n.Int(&z) 48 high.QuoRem(&z, decimalShift, &low) 49 return decimal128.New(high.Int64(), low.Uint64()), ok 50 } 51 52 func stringFloatToInt(src string, scale int64) (int64, bool) { 53 b, ok := new(big.Float).SetString(src) 54 if !ok { 55 return 0, ok 56 } 57 s := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(scale), nil)) 58 n := new(big.Float).Mul(b, s) 59 var z big.Int 60 n.Int(&z) 61 if !z.IsInt64() { 62 return 0, false 63 } 64 return z.Int64(), true 65 } 66 67 type tcGoTypeToSnowflake struct { 68 in interface{} 69 tmode snowflakeType 70 out snowflakeType 71 } 72 73 func TestGoTypeToSnowflake(t *testing.T) { 74 testcases := []tcGoTypeToSnowflake{ 75 {in: int64(123), tmode: nullType, out: fixedType}, 76 {in: float64(234.56), tmode: nullType, out: realType}, 77 {in: true, tmode: nullType, out: booleanType}, 78 {in: "teststring", tmode: nullType, out: textType}, 79 {in: Array([]int{1}), tmode: nullType, out: sliceType}, 80 {in: Array(&[]int{1}), tmode: nullType, out: sliceType}, 81 {in: Array([]int32{1}), tmode: nullType, out: sliceType}, 82 {in: Array(&[]int32{1}), tmode: nullType, out: sliceType}, 83 {in: Array([]int64{1}), tmode: nullType, out: sliceType}, 84 {in: Array(&[]int64{1}), tmode: nullType, out: sliceType}, 85 {in: Array([]float64{1.1}), tmode: nullType, out: sliceType}, 86 {in: Array(&[]float64{1.1}), tmode: nullType, out: sliceType}, 87 {in: Array([]float32{1.1}), tmode: nullType, out: sliceType}, 88 {in: Array(&[]float32{1.1}), tmode: nullType, out: sliceType}, 89 {in: Array([]bool{true}), tmode: nullType, out: sliceType}, 90 {in: Array([]string{"test string"}), tmode: nullType, out: sliceType}, 91 {in: Array([][]byte{}), tmode: nullType, out: sliceType}, 92 {in: Array([]time.Time{time.Now()}, TimestampNTZType), tmode: timestampNtzType, out: sliceType}, 93 {in: Array([]time.Time{time.Now()}, TimestampLTZType), tmode: timestampLtzType, out: sliceType}, 94 {in: Array([]time.Time{time.Now()}, TimestampTZType), tmode: timestampTzType, out: sliceType}, 95 {in: Array([]time.Time{time.Now()}, DateType), tmode: dateType, out: sliceType}, 96 {in: Array([]time.Time{time.Now()}, TimeType), tmode: timeType, out: sliceType}, 97 {in: DataTypeBinary, tmode: nullType, out: changeType}, 98 {in: DataTypeTimestampLtz, tmode: nullType, out: changeType}, 99 {in: DataTypeTimestampNtz, tmode: nullType, out: changeType}, 100 {in: DataTypeTimestampTz, tmode: nullType, out: changeType}, 101 {in: time.Now(), tmode: timestampNtzType, out: timestampNtzType}, 102 {in: time.Now(), tmode: timestampTzType, out: timestampTzType}, 103 {in: time.Now(), tmode: timestampLtzType, out: timestampLtzType}, 104 {in: []byte{1, 2, 3}, tmode: binaryType, out: binaryType}, 105 {in: Array([]interface{}{int64(123)}), tmode: nullType, out: sliceType}, 106 {in: Array([]interface{}{float64(234.56)}), tmode: nullType, out: sliceType}, 107 {in: Array([]interface{}{true}), tmode: nullType, out: sliceType}, 108 {in: Array([]interface{}{"teststring"}), tmode: nullType, out: sliceType}, 109 {in: Array([]interface{}{[]byte{1, 2, 3}}), tmode: nullType, out: sliceType}, 110 {in: Array([]interface{}{time.Now()}), tmode: timestampNtzType, out: sliceType}, 111 {in: Array([]interface{}{time.Now()}), tmode: timestampTzType, out: sliceType}, 112 {in: Array([]interface{}{time.Now()}), tmode: timestampLtzType, out: sliceType}, 113 {in: Array([]interface{}{time.Now()}), tmode: dateType, out: sliceType}, 114 {in: Array([]interface{}{time.Now()}), tmode: timeType, out: sliceType}, 115 {in: Array([]interface{}{time.Now()}, TimestampNTZType), tmode: timestampLtzType, out: sliceType}, 116 {in: Array([]interface{}{time.Now()}, TimestampLTZType), tmode: dateType, out: sliceType}, 117 {in: Array([]interface{}{time.Now()}, TimestampTZType), tmode: timeType, out: sliceType}, 118 {in: Array([]interface{}{time.Now()}, DateType), tmode: timestampNtzType, out: sliceType}, 119 {in: Array([]interface{}{time.Now()}, TimeType), tmode: timestampTzType, out: sliceType}, 120 // negative 121 {in: 123, tmode: nullType, out: unSupportedType}, 122 {in: int8(12), tmode: nullType, out: unSupportedType}, 123 {in: int32(456), tmode: nullType, out: unSupportedType}, 124 {in: uint(456), tmode: nullType, out: unSupportedType}, 125 {in: uint8(12), tmode: nullType, out: unSupportedType}, 126 {in: uint64(456), tmode: nullType, out: unSupportedType}, 127 {in: []byte{100}, tmode: nullType, out: unSupportedType}, 128 {in: []int{1}, tmode: nullType, out: unSupportedType}, 129 {in: nil, tmode: nullType, out: unSupportedType}, 130 } 131 for _, test := range testcases { 132 t.Run(fmt.Sprintf("%v_%v_%v", test.in, test.out, test.tmode), func(t *testing.T) { 133 a := goTypeToSnowflake(test.in, test.tmode) 134 if a != test.out { 135 t.Errorf("failed. in: %v, tmode: %v, expected: %v, got: %v", test.in, test.tmode, test.out, a) 136 } 137 }) 138 } 139 } 140 141 type tcSnowflakeTypeToGo struct { 142 in snowflakeType 143 scale int64 144 out reflect.Type 145 } 146 147 func TestSnowflakeTypeToGo(t *testing.T) { 148 testcases := []tcSnowflakeTypeToGo{ 149 {in: fixedType, scale: 0, out: reflect.TypeOf(int64(0))}, 150 {in: fixedType, scale: 2, out: reflect.TypeOf(float64(0))}, 151 {in: realType, scale: 0, out: reflect.TypeOf(float64(0))}, 152 {in: textType, scale: 0, out: reflect.TypeOf("")}, 153 {in: dateType, scale: 0, out: reflect.TypeOf(time.Now())}, 154 {in: timeType, scale: 0, out: reflect.TypeOf(time.Now())}, 155 {in: timestampLtzType, scale: 0, out: reflect.TypeOf(time.Now())}, 156 {in: timestampNtzType, scale: 0, out: reflect.TypeOf(time.Now())}, 157 {in: timestampTzType, scale: 0, out: reflect.TypeOf(time.Now())}, 158 {in: objectType, scale: 0, out: reflect.TypeOf("")}, 159 {in: variantType, scale: 0, out: reflect.TypeOf("")}, 160 {in: arrayType, scale: 0, out: reflect.TypeOf("")}, 161 {in: binaryType, scale: 0, out: reflect.TypeOf([]byte{})}, 162 {in: booleanType, scale: 0, out: reflect.TypeOf(true)}, 163 {in: sliceType, scale: 0, out: reflect.TypeOf("")}, 164 } 165 for _, test := range testcases { 166 t.Run(fmt.Sprintf("%v_%v", test.in, test.out), func(t *testing.T) { 167 a := snowflakeTypeToGo(test.in, test.scale) 168 if a != test.out { 169 t.Errorf("failed. in: %v, scale: %v, expected: %v, got: %v", 170 test.in, test.scale, test.out, a) 171 } 172 }) 173 } 174 } 175 176 func TestValueToString(t *testing.T) { 177 v := cmplx.Sqrt(-5 + 12i) // should never happen as Go sql package must have already validated. 178 _, err := valueToString(v, nullType) 179 if err == nil { 180 t.Errorf("should raise error: %v", v) 181 } 182 183 // both localTime and utcTime should yield the same unix timestamp 184 localTime := time.Date(2019, 2, 6, 14, 17, 31, 123456789, time.FixedZone("-08:00", -8*3600)) 185 utcTime := time.Date(2019, 2, 6, 22, 17, 31, 123456789, time.UTC) 186 expectedUnixTime := "1549491451123456789" // time.Unix(1549491451, 123456789).Format(time.RFC3339) == "2019-02-06T14:17:31-08:00" 187 expectedBool := "true" 188 expectedInt64 := "1" 189 expectedFloat64 := "1.1" 190 expectedString := "teststring" 191 192 if s, err := valueToString(localTime, timestampLtzType); err != nil { 193 t.Error("unexpected error") 194 } else if s == nil { 195 t.Errorf("expected '%v', got %v", expectedUnixTime, s) 196 } else if *s != expectedUnixTime { 197 t.Errorf("expected '%v', got '%v'", expectedUnixTime, *s) 198 } 199 200 if s, err := valueToString(utcTime, timestampLtzType); err != nil { 201 t.Error("unexpected error") 202 } else if s == nil { 203 t.Errorf("expected '%v', got %v", expectedUnixTime, s) 204 } else if *s != expectedUnixTime { 205 t.Errorf("expected '%v', got '%v'", expectedUnixTime, *s) 206 } 207 208 if s, err := valueToString(sql.NullBool{Bool: true, Valid: true}, timestampLtzType); err != nil { 209 t.Error("unexpected error") 210 } else if s == nil { 211 t.Errorf("expected '%v', got %v", expectedBool, s) 212 } else if *s != expectedBool { 213 t.Errorf("expected '%v', got '%v'", expectedBool, *s) 214 } 215 216 if s, err := valueToString(sql.NullInt64{Int64: 1, Valid: true}, timestampLtzType); err != nil { 217 t.Error("unexpected error") 218 } else if s == nil { 219 t.Errorf("expected '%v', got %v", expectedInt64, s) 220 } else if *s != expectedInt64 { 221 t.Errorf("expected '%v', got '%v'", expectedInt64, *s) 222 } 223 224 if s, err := valueToString(sql.NullFloat64{Float64: 1.1, Valid: true}, timestampLtzType); err != nil { 225 t.Error("unexpected error") 226 } else if s == nil { 227 t.Errorf("expected '%v', got %v", expectedFloat64, s) 228 } else if *s != expectedFloat64 { 229 t.Errorf("expected '%v', got '%v'", expectedFloat64, *s) 230 } 231 232 if s, err := valueToString(sql.NullString{String: "teststring", Valid: true}, timestampLtzType); err != nil { 233 t.Error("unexpected error") 234 } else if s == nil { 235 t.Errorf("expected '%v', got %v", expectedString, s) 236 } else if *s != expectedString { 237 t.Errorf("expected '%v', got '%v'", expectedString, *s) 238 } 239 } 240 241 func TestExtractTimestamp(t *testing.T) { 242 s := "1234abcdef" // pragma: allowlist secret 243 _, _, err := extractTimestamp(&s) 244 if err == nil { 245 t.Errorf("should raise error: %v", s) 246 } 247 s = "1234abc.def" 248 _, _, err = extractTimestamp(&s) 249 if err == nil { 250 t.Errorf("should raise error: %v", s) 251 } 252 s = "1234.def" 253 _, _, err = extractTimestamp(&s) 254 if err == nil { 255 t.Errorf("should raise error: %v", s) 256 } 257 } 258 259 func TestStringToValue(t *testing.T) { 260 var source string 261 var dest driver.Value 262 var err error 263 var rowType *execResponseRowType 264 source = "abcdefg" 265 266 types := []string{ 267 "date", "time", "timestamp_ntz", "timestamp_ltz", "timestamp_tz", "binary", 268 } 269 270 for _, tt := range types { 271 t.Run(tt, func(t *testing.T) { 272 rowType = &execResponseRowType{ 273 Type: tt, 274 } 275 if err = stringToValue(&dest, *rowType, &source, nil); err == nil { 276 t.Errorf("should raise error. type: %v, value:%v", tt, source) 277 } 278 }) 279 } 280 281 sources := []string{ 282 "12345K78 2020", 283 "12345678 20T0", 284 } 285 286 types = []string{ 287 "timestamp_tz", 288 } 289 290 for _, ss := range sources { 291 for _, tt := range types { 292 t.Run(ss+tt, func(t *testing.T) { 293 rowType = &execResponseRowType{ 294 Type: tt, 295 } 296 if err = stringToValue(&dest, *rowType, &ss, nil); err == nil { 297 t.Errorf("should raise error. type: %v, value:%v", tt, source) 298 } 299 }) 300 } 301 } 302 303 src := "1549491451.123456789" 304 if err = stringToValue(&dest, execResponseRowType{Type: "timestamp_ltz"}, &src, nil); err != nil { 305 t.Errorf("unexpected error: %v", err) 306 } else if ts, ok := dest.(time.Time); !ok { 307 t.Errorf("expected type: 'time.Time', got '%v'", reflect.TypeOf(dest)) 308 } else if ts.UnixNano() != 1549491451123456789 { 309 t.Errorf("expected unix timestamp: 1549491451123456789, got %v", ts.UnixNano()) 310 } 311 } 312 313 type tcArrayToString struct { 314 in driver.NamedValue 315 typ snowflakeType 316 out []string 317 } 318 319 func TestArrayToString(t *testing.T) { 320 testcases := []tcArrayToString{ 321 {in: driver.NamedValue{Value: &intArray{1, 2}}, typ: fixedType, out: []string{"1", "2"}}, 322 {in: driver.NamedValue{Value: &int32Array{1, 2}}, typ: fixedType, out: []string{"1", "2"}}, 323 {in: driver.NamedValue{Value: &int64Array{3, 4, 5}}, typ: fixedType, out: []string{"3", "4", "5"}}, 324 {in: driver.NamedValue{Value: &float64Array{6.7}}, typ: realType, out: []string{"6.7"}}, 325 {in: driver.NamedValue{Value: &float32Array{1.5}}, typ: realType, out: []string{"1.5"}}, 326 {in: driver.NamedValue{Value: &boolArray{true, false}}, typ: booleanType, out: []string{"true", "false"}}, 327 {in: driver.NamedValue{Value: &stringArray{"foo", "bar", "baz"}}, typ: textType, out: []string{"foo", "bar", "baz"}}, 328 } 329 for _, test := range testcases { 330 t.Run(strings.Join(test.out, "_"), func(t *testing.T) { 331 s, a := snowflakeArrayToString(&test.in, false) 332 if s != test.typ { 333 t.Errorf("failed. in: %v, expected: %v, got: %v", test.in, test.typ, s) 334 } 335 for i, v := range a { 336 if *v != test.out[i] { 337 t.Errorf("failed. in: %v, expected: %v, got: %v", test.in, test.out[i], a) 338 } 339 } 340 }) 341 } 342 } 343 344 func TestArrowToValue(t *testing.T) { 345 dest := make([]snowflakeValue, 2) 346 347 pool := memory.NewCheckedAllocator(memory.NewGoAllocator()) 348 defer pool.AssertSize(t, 0) 349 var valids []bool // AppendValues() with an empty valid array adds every value by default 350 351 localTime := time.Date(2019, 2, 6, 14, 17, 31, 123456789, time.FixedZone("-08:00", -8*3600)) 352 353 field1 := arrow.Field{Name: "epoch", Type: &arrow.Int64Type{}} 354 field2 := arrow.Field{Name: "timezone", Type: &arrow.Int32Type{}} 355 tzStruct := arrow.StructOf(field1, field2) 356 357 type testObj struct { 358 field1 int 359 field2 string 360 } 361 362 for _, tc := range []struct { 363 logical string 364 physical string 365 rowType execResponseRowType 366 values interface{} 367 builder array.Builder 368 append func(b array.Builder, vs interface{}) 369 compare func(src interface{}, dst []snowflakeValue) int 370 higherPrecision bool 371 }{ 372 { 373 logical: "fixed", 374 physical: "number", // default: number(38, 0) 375 values: []int64{1, 2}, 376 builder: array.NewInt64Builder(pool), 377 append: func(b array.Builder, vs interface{}) { b.(*array.Int64Builder).AppendValues(vs.([]int64), valids) }, 378 higherPrecision: true, 379 }, 380 { 381 logical: "fixed", 382 physical: "number(38,5)", 383 rowType: execResponseRowType{Scale: 5}, 384 values: []string{"1.05430", "2.08983"}, 385 builder: array.NewInt64Builder(pool), 386 append: func(b array.Builder, vs interface{}) { 387 for _, s := range vs.([]string) { 388 num, ok := stringFloatToInt(s, 5) 389 if !ok { 390 t.Fatalf("failed to convert to int") 391 } 392 b.(*array.Int64Builder).Append(num) 393 } 394 }, 395 compare: func(src interface{}, dst []snowflakeValue) int { 396 srcvs := src.([]string) 397 for i := range srcvs { 398 num, ok := stringFloatToInt(srcvs[i], 5) 399 if !ok { 400 return i 401 } 402 srcDec := intToBigFloat(num, 5) 403 dstDec := dst[i].(*big.Float) 404 if srcDec.Cmp(dstDec) != 0 { 405 return i 406 } 407 } 408 return -1 409 }, 410 higherPrecision: true, 411 }, 412 { 413 logical: "fixed", 414 physical: "number(38,5)", 415 rowType: execResponseRowType{Scale: 5}, 416 values: []string{"1.05430", "2.08983"}, 417 builder: array.NewInt64Builder(pool), 418 append: func(b array.Builder, vs interface{}) { 419 for _, s := range vs.([]string) { 420 num, ok := stringFloatToInt(s, 5) 421 if !ok { 422 t.Fatalf("failed to convert to int") 423 } 424 b.(*array.Int64Builder).Append(num) 425 } 426 }, 427 compare: func(src interface{}, dst []snowflakeValue) int { 428 srcvs := src.([]string) 429 for i := range srcvs { 430 num, ok := stringFloatToInt(srcvs[i], 5) 431 if !ok { 432 return i 433 } 434 srcDec := fmt.Sprintf("%.*f", 5, float64(num)/math.Pow10(int(5))) 435 dstDec := dst[i] 436 if srcDec != dstDec { 437 return i 438 } 439 } 440 return -1 441 }, 442 higherPrecision: false, 443 }, 444 { 445 logical: "fixed", 446 physical: "number(38,0)", 447 values: []string{"10000000000000000000000000000000000000", "-12345678901234567890123456789012345678"}, 448 builder: array.NewDecimal128Builder(pool, &arrow.Decimal128Type{Precision: 30, Scale: 2}), 449 append: func(b array.Builder, vs interface{}) { 450 for _, s := range vs.([]string) { 451 num, ok := stringIntToDecimal(s) 452 if !ok { 453 t.Fatalf("failed to convert to big.Int") 454 } 455 b.(*array.Decimal128Builder).Append(num) 456 } 457 }, 458 compare: func(src interface{}, dst []snowflakeValue) int { 459 srcvs := src.([]string) 460 for i := range srcvs { 461 num, ok := stringIntToDecimal(srcvs[i]) 462 if !ok { 463 return i 464 } 465 srcDec := decimalToBigInt(num) 466 dstDec := dst[i].(*big.Int) 467 if srcDec.Cmp(dstDec) != 0 { 468 return i 469 } 470 } 471 return -1 472 }, 473 higherPrecision: true, 474 }, 475 { 476 logical: "fixed", 477 physical: "number(38,37)", 478 rowType: execResponseRowType{Scale: 37}, 479 values: []string{"1.2345678901234567890123456789012345678", "-9.9999999999999999999999999999999999999"}, 480 builder: array.NewDecimal128Builder(pool, &arrow.Decimal128Type{Precision: 38, Scale: 37}), 481 append: func(b array.Builder, vs interface{}) { 482 for _, s := range vs.([]string) { 483 num, ok := stringFloatToDecimal(s, 37) 484 if !ok { 485 t.Fatalf("failed to convert to big.Rat") 486 } 487 b.(*array.Decimal128Builder).Append(num) 488 } 489 }, 490 compare: func(src interface{}, dst []snowflakeValue) int { 491 srcvs := src.([]string) 492 for i := range srcvs { 493 num, ok := stringFloatToDecimal(srcvs[i], 37) 494 if !ok { 495 return i 496 } 497 srcDec := decimalToBigFloat(num, 37) 498 dstDec := dst[i].(*big.Float) 499 if srcDec.Cmp(dstDec) != 0 { 500 return i 501 } 502 } 503 return -1 504 }, 505 higherPrecision: true, 506 }, 507 { 508 logical: "fixed", 509 physical: "int8", 510 values: []int8{1, 2}, 511 builder: array.NewInt8Builder(pool), 512 append: func(b array.Builder, vs interface{}) { b.(*array.Int8Builder).AppendValues(vs.([]int8), valids) }, 513 compare: func(src interface{}, dst []snowflakeValue) int { 514 srcvs := src.([]int8) 515 for i := range srcvs { 516 if int64(srcvs[i]) != dst[i].(int64) { 517 return i 518 } 519 } 520 return -1 521 }, 522 higherPrecision: true, 523 }, 524 { 525 logical: "fixed", 526 physical: "int16", 527 values: []int16{1, 2}, 528 builder: array.NewInt16Builder(pool), 529 append: func(b array.Builder, vs interface{}) { b.(*array.Int16Builder).AppendValues(vs.([]int16), valids) }, 530 compare: func(src interface{}, dst []snowflakeValue) int { 531 srcvs := src.([]int16) 532 for i := range srcvs { 533 if int64(srcvs[i]) != dst[i].(int64) { 534 return i 535 } 536 } 537 return -1 538 }, 539 higherPrecision: true, 540 }, 541 { 542 logical: "fixed", 543 physical: "int16", 544 values: []string{"1.2345", "2.3456"}, 545 rowType: execResponseRowType{Scale: 4}, 546 builder: array.NewInt16Builder(pool), 547 append: func(b array.Builder, vs interface{}) { 548 for _, s := range vs.([]string) { 549 num, ok := stringFloatToInt(s, 4) 550 if !ok { 551 t.Fatalf("failed to convert to int") 552 } 553 b.(*array.Int16Builder).Append(int16(num)) 554 } 555 }, 556 compare: func(src interface{}, dst []snowflakeValue) int { 557 srcvs := src.([]string) 558 for i := range srcvs { 559 num, ok := stringFloatToInt(srcvs[i], 4) 560 if !ok { 561 return i 562 } 563 srcDec := intToBigFloat(num, 4) 564 dstDec := dst[i].(*big.Float) 565 if srcDec.Cmp(dstDec) != 0 { 566 return i 567 } 568 } 569 return -1 570 }, 571 higherPrecision: true, 572 }, 573 { 574 logical: "fixed", 575 physical: "int16", 576 values: []string{"1.2345", "2.3456"}, 577 rowType: execResponseRowType{Scale: 4}, 578 builder: array.NewInt16Builder(pool), 579 append: func(b array.Builder, vs interface{}) { 580 for _, s := range vs.([]string) { 581 num, ok := stringFloatToInt(s, 4) 582 if !ok { 583 t.Fatalf("failed to convert to int") 584 } 585 b.(*array.Int16Builder).Append(int16(num)) 586 } 587 }, 588 compare: func(src interface{}, dst []snowflakeValue) int { 589 srcvs := src.([]string) 590 for i := range srcvs { 591 num, ok := stringFloatToInt(srcvs[i], 4) 592 if !ok { 593 return i 594 } 595 srcDec := fmt.Sprintf("%.*f", 4, float64(num)/math.Pow10(int(4))) 596 dstDec := dst[i] 597 if srcDec != dstDec { 598 return i 599 } 600 } 601 return -1 602 }, 603 higherPrecision: false, 604 }, 605 { 606 logical: "fixed", 607 physical: "int32", 608 values: []int32{1, 2}, 609 builder: array.NewInt32Builder(pool), 610 append: func(b array.Builder, vs interface{}) { b.(*array.Int32Builder).AppendValues(vs.([]int32), valids) }, 611 compare: func(src interface{}, dst []snowflakeValue) int { 612 srcvs := src.([]int32) 613 for i := range srcvs { 614 if int64(srcvs[i]) != dst[i].(int64) { 615 return i 616 } 617 } 618 return -1 619 }, 620 higherPrecision: true, 621 }, 622 { 623 logical: "fixed", 624 physical: "int32", 625 values: []string{"1.23456", "2.34567"}, 626 rowType: execResponseRowType{Scale: 5}, 627 builder: array.NewInt32Builder(pool), 628 append: func(b array.Builder, vs interface{}) { 629 for _, s := range vs.([]string) { 630 num, ok := stringFloatToInt(s, 5) 631 if !ok { 632 t.Fatalf("failed to convert to int") 633 } 634 b.(*array.Int32Builder).Append(int32(num)) 635 } 636 }, 637 compare: func(src interface{}, dst []snowflakeValue) int { 638 srcvs := src.([]string) 639 for i := range srcvs { 640 num, ok := stringFloatToInt(srcvs[i], 5) 641 if !ok { 642 return i 643 } 644 srcDec := intToBigFloat(num, 5) 645 dstDec := dst[i].(*big.Float) 646 if srcDec.Cmp(dstDec) != 0 { 647 return i 648 } 649 } 650 return -1 651 }, 652 higherPrecision: true, 653 }, 654 { 655 logical: "fixed", 656 physical: "int32", 657 values: []string{"1.23456", "2.34567"}, 658 rowType: execResponseRowType{Scale: 5}, 659 builder: array.NewInt32Builder(pool), 660 append: func(b array.Builder, vs interface{}) { 661 for _, s := range vs.([]string) { 662 num, ok := stringFloatToInt(s, 5) 663 if !ok { 664 t.Fatalf("failed to convert to int") 665 } 666 b.(*array.Int32Builder).Append(int32(num)) 667 } 668 }, 669 compare: func(src interface{}, dst []snowflakeValue) int { 670 srcvs := src.([]string) 671 for i := range srcvs { 672 num, ok := stringFloatToInt(srcvs[i], 5) 673 if !ok { 674 return i 675 } 676 srcDec := fmt.Sprintf("%.*f", 5, float64(num)/math.Pow10(int(5))) 677 dstDec := dst[i] 678 if srcDec != dstDec { 679 return i 680 } 681 } 682 return -1 683 }, 684 higherPrecision: false, 685 }, 686 { 687 logical: "fixed", 688 physical: "int64", 689 values: []int64{1, 2}, 690 builder: array.NewInt64Builder(pool), 691 append: func(b array.Builder, vs interface{}) { b.(*array.Int64Builder).AppendValues(vs.([]int64), valids) }, 692 higherPrecision: true, 693 }, 694 { 695 logical: "boolean", 696 values: []bool{true, false}, 697 builder: array.NewBooleanBuilder(pool), 698 append: func(b array.Builder, vs interface{}) { b.(*array.BooleanBuilder).AppendValues(vs.([]bool), valids) }, 699 }, 700 { 701 logical: "real", 702 physical: "float", 703 values: []float64{1, 2}, 704 builder: array.NewFloat64Builder(pool), 705 append: func(b array.Builder, vs interface{}) { b.(*array.Float64Builder).AppendValues(vs.([]float64), valids) }, 706 }, 707 { 708 logical: "text", 709 physical: "string", 710 values: []string{"foo", "bar"}, 711 builder: array.NewStringBuilder(pool), 712 append: func(b array.Builder, vs interface{}) { b.(*array.StringBuilder).AppendValues(vs.([]string), valids) }, 713 }, 714 { 715 logical: "binary", 716 values: [][]byte{[]byte("foo"), []byte("bar")}, 717 builder: array.NewBinaryBuilder(pool, arrow.BinaryTypes.Binary), 718 append: func(b array.Builder, vs interface{}) { b.(*array.BinaryBuilder).AppendValues(vs.([][]byte), valids) }, 719 }, 720 { 721 logical: "date", 722 values: []time.Time{time.Now(), localTime}, 723 builder: array.NewDate32Builder(pool), 724 append: func(b array.Builder, vs interface{}) { 725 for _, d := range vs.([]time.Time) { 726 b.(*array.Date32Builder).Append(arrow.Date32(d.Unix())) 727 } 728 }, 729 }, 730 { 731 logical: "time", 732 values: []time.Time{time.Now(), time.Now()}, 733 rowType: execResponseRowType{Scale: 9}, 734 builder: array.NewInt64Builder(pool), 735 append: func(b array.Builder, vs interface{}) { 736 for _, t := range vs.([]time.Time) { 737 b.(*array.Int64Builder).Append(t.UnixNano()) 738 } 739 }, 740 compare: func(src interface{}, dst []snowflakeValue) int { 741 srcvs := src.([]time.Time) 742 for i := range srcvs { 743 if srcvs[i].Nanosecond() != dst[i].(time.Time).Nanosecond() { 744 return i 745 } 746 } 747 return -1 748 }, 749 higherPrecision: true, 750 }, 751 { 752 logical: "timestamp_ntz", 753 values: []time.Time{time.Now(), localTime}, 754 rowType: execResponseRowType{Scale: 9}, 755 builder: array.NewInt64Builder(pool), 756 append: func(b array.Builder, vs interface{}) { 757 for _, t := range vs.([]time.Time) { 758 b.(*array.Int64Builder).Append(t.UnixNano()) 759 } 760 }, 761 compare: func(src interface{}, dst []snowflakeValue) int { 762 srcvs := src.([]time.Time) 763 for i := range srcvs { 764 if srcvs[i].UnixNano() != dst[i].(time.Time).UnixNano() { 765 return i 766 } 767 } 768 return -1 769 }, 770 }, 771 { 772 logical: "timestamp_ltz", 773 values: []time.Time{time.Now(), localTime}, 774 rowType: execResponseRowType{Scale: 9}, 775 builder: array.NewInt64Builder(pool), 776 append: func(b array.Builder, vs interface{}) { 777 for _, t := range vs.([]time.Time) { 778 b.(*array.Int64Builder).Append(t.UnixNano()) 779 } 780 }, 781 compare: func(src interface{}, dst []snowflakeValue) int { 782 srcvs := src.([]time.Time) 783 for i := range srcvs { 784 if srcvs[i].UnixNano() != dst[i].(time.Time).UnixNano() { 785 return i 786 } 787 } 788 return -1 789 }, 790 }, 791 { 792 logical: "timestamp_tz", 793 values: []time.Time{time.Now(), localTime}, 794 builder: array.NewStructBuilder(pool, tzStruct), 795 append: func(b array.Builder, vs interface{}) { 796 sb := b.(*array.StructBuilder) 797 valids = []bool{true, true} 798 sb.AppendValues(valids) 799 for _, t := range vs.([]time.Time) { 800 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 801 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.UnixNano())) 802 } 803 }, 804 compare: func(src interface{}, dst []snowflakeValue) int { 805 srcvs := src.([]time.Time) 806 for i := range srcvs { 807 if srcvs[i].Unix() != dst[i].(time.Time).Unix() { 808 return i 809 } 810 } 811 return -1 812 }, 813 }, 814 { 815 logical: "array", 816 values: [][]string{{"foo", "bar"}, {"baz", "quz", "quux"}}, 817 builder: array.NewStringBuilder(pool), 818 append: func(b array.Builder, vs interface{}) { 819 for _, a := range vs.([][]string) { 820 b.(*array.StringBuilder).Append(fmt.Sprint(a)) 821 } 822 }, 823 compare: func(src interface{}, dst []snowflakeValue) int { 824 srcvs := src.([][]string) 825 for i, o := range srcvs { 826 if fmt.Sprint(o) != dst[i].(string) { 827 return i 828 } 829 } 830 return -1 831 }, 832 }, 833 { 834 logical: "object", 835 values: []testObj{{0, "foo"}, {1, "bar"}}, 836 builder: array.NewStringBuilder(pool), 837 append: func(b array.Builder, vs interface{}) { 838 for _, o := range vs.([]testObj) { 839 b.(*array.StringBuilder).Append(fmt.Sprint(o)) 840 } 841 }, 842 compare: func(src interface{}, dst []snowflakeValue) int { 843 srcvs := src.([]testObj) 844 for i, o := range srcvs { 845 if fmt.Sprint(o) != dst[i].(string) { 846 return i 847 } 848 } 849 return -1 850 }, 851 }, 852 } { 853 testName := tc.logical 854 if tc.physical != "" { 855 testName += " " + tc.physical 856 } 857 t.Run(testName, func(t *testing.T) { 858 b := tc.builder 859 tc.append(b, tc.values) 860 arr := b.NewArray() 861 defer arr.Release() 862 863 meta := tc.rowType 864 meta.Type = tc.logical 865 866 withHigherPrecision := tc.higherPrecision 867 868 if err := arrowToValue(dest, meta, arr, localTime.Location(), withHigherPrecision); err != nil { 869 t.Fatalf("error: %s", err) 870 } 871 872 elemType := reflect.TypeOf(tc.values).Elem() 873 if tc.compare != nil { 874 idx := tc.compare(tc.values, dest) 875 if idx != -1 { 876 t.Fatalf("error: column array value mistmatch at index %v", idx) 877 } 878 } else { 879 for _, d := range dest { 880 if reflect.TypeOf(d) != elemType { 881 t.Fatalf("error: expected type %s, got type %s", reflect.TypeOf(d), elemType) 882 } 883 } 884 } 885 }) 886 887 } 888 } 889 890 func TestArrowToRecord(t *testing.T) { 891 pool := memory.NewCheckedAllocator(memory.NewGoAllocator()) 892 defer pool.AssertSize(t, 0) // ensure no arrow memory leaks 893 var valids []bool // AppendValues() with an empty valid array adds every value by default 894 895 localTime := time.Date(2019, 1, 1, 1, 17, 31, 123456789, time.FixedZone("-08:00", -8*3600)) 896 localTimeFarIntoFuture := time.Date(9000, 2, 6, 14, 17, 31, 123456789, time.FixedZone("-08:00", -8*3600)) 897 898 epochField := arrow.Field{Name: "epoch", Type: &arrow.Int64Type{}} 899 timezoneField := arrow.Field{Name: "timezone", Type: &arrow.Int32Type{}} 900 fractionField := arrow.Field{Name: "fraction", Type: &arrow.Int32Type{}} 901 timestampTzStructWithoutFraction := arrow.StructOf(epochField, timezoneField) 902 timestampTzStructWithFraction := arrow.StructOf(epochField, fractionField, timezoneField) 903 timestampNtzStruct := arrow.StructOf(epochField, fractionField) 904 timestampLtzStruct := arrow.StructOf(epochField, fractionField) 905 906 type testObj struct { 907 field1 int 908 field2 string 909 } 910 911 for _, tc := range []struct { 912 logical string 913 physical string 914 sc *arrow.Schema 915 rowType execResponseRowType 916 values interface{} 917 expected interface{} 918 error string 919 arrowBatchesTimestampOption snowflakeArrowBatchesTimestampOption 920 enableArrowBatchesUtf8Validation bool 921 withHigherPrecision bool 922 nrows int 923 builder array.Builder 924 append func(b array.Builder, vs interface{}) 925 compare func(src interface{}, expected interface{}, rec arrow.Record) int 926 }{ 927 { 928 logical: "fixed", 929 physical: "number", // default: number(38, 0) 930 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 931 values: []int64{1, 2}, 932 nrows: 2, 933 builder: array.NewInt64Builder(pool), 934 append: func(b array.Builder, vs interface{}) { b.(*array.Int64Builder).AppendValues(vs.([]int64), valids) }, 935 }, 936 { 937 logical: "fixed", 938 physical: "int64", 939 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Decimal128Type{Precision: 38, Scale: 0}}}, nil), 940 values: []string{"10000000000000000000000000000000000000", "-12345678901234567890123456789012345678"}, 941 nrows: 2, 942 builder: array.NewDecimal128Builder(pool, &arrow.Decimal128Type{Precision: 38, Scale: 0}), 943 append: func(b array.Builder, vs interface{}) { 944 for _, s := range vs.([]string) { 945 num, ok := stringIntToDecimal(s) 946 if !ok { 947 t.Fatalf("failed to convert to Int64") 948 } 949 b.(*array.Decimal128Builder).Append(num) 950 } 951 }, 952 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 953 srcvs := src.([]string) 954 for i, dec := range convertedRec.Column(0).(*array.Int64).Int64Values() { 955 num, ok := stringIntToDecimal(srcvs[i]) 956 if !ok { 957 return i 958 } 959 srcDec := decimalToBigInt(num).Int64() 960 if srcDec != dec { 961 return i 962 } 963 } 964 return -1 965 }, 966 }, 967 { 968 logical: "fixed", 969 physical: "number(38,0)", 970 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Decimal128Type{Precision: 38, Scale: 0}}}, nil), 971 values: []string{"10000000000000000000000000000000000000", "-12345678901234567890123456789012345678"}, 972 withHigherPrecision: true, 973 nrows: 2, 974 builder: array.NewDecimal128Builder(pool, &arrow.Decimal128Type{Precision: 38, Scale: 0}), 975 append: func(b array.Builder, vs interface{}) { 976 for _, s := range vs.([]string) { 977 num, ok := stringIntToDecimal(s) 978 if !ok { 979 t.Fatalf("failed to convert to Int64") 980 } 981 b.(*array.Decimal128Builder).Append(num) 982 } 983 }, 984 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 985 srcvs := src.([]string) 986 for i, dec := range convertedRec.Column(0).(*array.Decimal128).Values() { 987 srcDec, ok := stringIntToDecimal(srcvs[i]) 988 if !ok { 989 return i 990 } 991 if srcDec != dec { 992 return i 993 } 994 } 995 return -1 996 }, 997 }, 998 { 999 logical: "fixed", 1000 physical: "float64", 1001 rowType: execResponseRowType{Scale: 37}, 1002 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Decimal128Type{Precision: 38, Scale: 37}}}, nil), 1003 values: []string{"1.2345678901234567890123456789012345678", "-9.999999999999999"}, 1004 nrows: 2, 1005 builder: array.NewDecimal128Builder(pool, &arrow.Decimal128Type{Precision: 38, Scale: 37}), 1006 append: func(b array.Builder, vs interface{}) { 1007 for _, s := range vs.([]string) { 1008 num, err := decimal128.FromString(s, 38, 37) 1009 if err != nil { 1010 t.Fatalf("failed to convert to decimal: %s", err) 1011 } 1012 b.(*array.Decimal128Builder).Append(num) 1013 } 1014 }, 1015 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1016 srcvs := src.([]string) 1017 for i, dec := range convertedRec.Column(0).(*array.Float64).Float64Values() { 1018 num, err := decimal128.FromString(srcvs[i], 38, 37) 1019 if err != nil { 1020 return i 1021 } 1022 srcDec := num.ToFloat64(37) 1023 if srcDec != dec { 1024 return i 1025 } 1026 } 1027 return -1 1028 }, 1029 }, 1030 { 1031 logical: "fixed", 1032 physical: "number(38,37)", 1033 rowType: execResponseRowType{Scale: 37}, 1034 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Decimal128Type{Precision: 38, Scale: 37}}}, nil), 1035 values: []string{"1.2345678901234567890123456789012345678", "-9.999999999999999"}, 1036 withHigherPrecision: true, 1037 nrows: 2, 1038 builder: array.NewDecimal128Builder(pool, &arrow.Decimal128Type{Precision: 38, Scale: 37}), 1039 append: func(b array.Builder, vs interface{}) { 1040 for _, s := range vs.([]string) { 1041 num, err := decimal128.FromString(s, 38, 37) 1042 if err != nil { 1043 t.Fatalf("failed to convert to decimal: %s", err) 1044 } 1045 b.(*array.Decimal128Builder).Append(num) 1046 } 1047 }, 1048 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1049 srcvs := src.([]string) 1050 for i, dec := range convertedRec.Column(0).(*array.Decimal128).Values() { 1051 srcDec, err := decimal128.FromString(srcvs[i], 38, 37) 1052 if err != nil { 1053 return i 1054 } 1055 if srcDec != dec { 1056 return i 1057 } 1058 } 1059 return -1 1060 }, 1061 }, 1062 { 1063 logical: "fixed", 1064 physical: "int8", 1065 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int8Type{}}}, nil), 1066 values: []int8{1, 2}, 1067 nrows: 2, 1068 builder: array.NewInt8Builder(pool), 1069 append: func(b array.Builder, vs interface{}) { b.(*array.Int8Builder).AppendValues(vs.([]int8), valids) }, 1070 }, 1071 { 1072 logical: "fixed", 1073 physical: "int16", 1074 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int16Type{}}}, nil), 1075 values: []int16{1, 2}, 1076 nrows: 2, 1077 builder: array.NewInt16Builder(pool), 1078 append: func(b array.Builder, vs interface{}) { b.(*array.Int16Builder).AppendValues(vs.([]int16), valids) }, 1079 }, 1080 { 1081 logical: "fixed", 1082 physical: "int32", 1083 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int32Type{}}}, nil), 1084 values: []int32{1, 2}, 1085 nrows: 2, 1086 builder: array.NewInt32Builder(pool), 1087 append: func(b array.Builder, vs interface{}) { b.(*array.Int32Builder).AppendValues(vs.([]int32), valids) }, 1088 }, 1089 { 1090 logical: "fixed", 1091 physical: "int64", 1092 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1093 values: []int64{1, 2}, 1094 nrows: 2, 1095 builder: array.NewInt64Builder(pool), 1096 append: func(b array.Builder, vs interface{}) { b.(*array.Int64Builder).AppendValues(vs.([]int64), valids) }, 1097 }, 1098 { 1099 logical: "fixed", 1100 physical: "float8", 1101 rowType: execResponseRowType{Scale: 1}, 1102 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int8Type{}}}, nil), 1103 values: []int8{10, 16}, 1104 nrows: 2, 1105 builder: array.NewInt8Builder(pool), 1106 append: func(b array.Builder, vs interface{}) { b.(*array.Int8Builder).AppendValues(vs.([]int8), valids) }, 1107 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1108 srcvs := src.([]int8) 1109 for i, f := range convertedRec.Column(0).(*array.Float64).Float64Values() { 1110 rawFloat, _ := intToBigFloat(int64(srcvs[i]), 1).Float64() 1111 if rawFloat != f { 1112 return i 1113 } 1114 } 1115 return -1 1116 }, 1117 }, 1118 { 1119 logical: "fixed", 1120 physical: "int8", 1121 rowType: execResponseRowType{Scale: 1}, 1122 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int8Type{}}}, nil), 1123 values: []int8{10, 16}, 1124 withHigherPrecision: true, 1125 nrows: 2, 1126 builder: array.NewInt8Builder(pool), 1127 append: func(b array.Builder, vs interface{}) { b.(*array.Int8Builder).AppendValues(vs.([]int8), valids) }, 1128 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1129 srcvs := src.([]int8) 1130 for i, f := range convertedRec.Column(0).(*array.Int8).Int8Values() { 1131 if srcvs[i] != f { 1132 return i 1133 } 1134 } 1135 return -1 1136 }, 1137 }, 1138 { 1139 logical: "fixed", 1140 physical: "float16", 1141 rowType: execResponseRowType{Scale: 1}, 1142 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int16Type{}}}, nil), 1143 values: []int16{20, 26}, 1144 nrows: 2, 1145 builder: array.NewInt16Builder(pool), 1146 append: func(b array.Builder, vs interface{}) { b.(*array.Int16Builder).AppendValues(vs.([]int16), valids) }, 1147 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1148 srcvs := src.([]int16) 1149 for i, f := range convertedRec.Column(0).(*array.Float64).Float64Values() { 1150 rawFloat, _ := intToBigFloat(int64(srcvs[i]), 1).Float64() 1151 if rawFloat != f { 1152 return i 1153 } 1154 } 1155 return -1 1156 }, 1157 }, 1158 { 1159 logical: "fixed", 1160 physical: "int16", 1161 rowType: execResponseRowType{Scale: 1}, 1162 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int16Type{}}}, nil), 1163 values: []int16{20, 26}, 1164 withHigherPrecision: true, 1165 nrows: 2, 1166 builder: array.NewInt16Builder(pool), 1167 append: func(b array.Builder, vs interface{}) { b.(*array.Int16Builder).AppendValues(vs.([]int16), valids) }, 1168 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1169 srcvs := src.([]int16) 1170 for i, f := range convertedRec.Column(0).(*array.Int16).Int16Values() { 1171 if srcvs[i] != f { 1172 return i 1173 } 1174 } 1175 return -1 1176 }, 1177 }, 1178 { 1179 logical: "fixed", 1180 physical: "float32", 1181 rowType: execResponseRowType{Scale: 2}, 1182 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int32Type{}}}, nil), 1183 values: []int32{200, 265}, 1184 nrows: 2, 1185 builder: array.NewInt32Builder(pool), 1186 append: func(b array.Builder, vs interface{}) { b.(*array.Int32Builder).AppendValues(vs.([]int32), valids) }, 1187 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1188 srcvs := src.([]int32) 1189 for i, f := range convertedRec.Column(0).(*array.Float64).Float64Values() { 1190 rawFloat, _ := intToBigFloat(int64(srcvs[i]), 2).Float64() 1191 if rawFloat != f { 1192 return i 1193 } 1194 } 1195 return -1 1196 }, 1197 }, 1198 { 1199 logical: "fixed", 1200 physical: "int32", 1201 rowType: execResponseRowType{Scale: 2}, 1202 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int32Type{}}}, nil), 1203 values: []int32{200, 265}, 1204 withHigherPrecision: true, 1205 nrows: 2, 1206 builder: array.NewInt32Builder(pool), 1207 append: func(b array.Builder, vs interface{}) { b.(*array.Int32Builder).AppendValues(vs.([]int32), valids) }, 1208 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1209 srcvs := src.([]int32) 1210 for i, f := range convertedRec.Column(0).(*array.Int32).Int32Values() { 1211 if srcvs[i] != f { 1212 return i 1213 } 1214 } 1215 return -1 1216 }, 1217 }, 1218 { 1219 logical: "fixed", 1220 physical: "float64", 1221 rowType: execResponseRowType{Scale: 5}, 1222 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1223 values: []int64{12345, 234567}, 1224 nrows: 2, 1225 builder: array.NewInt64Builder(pool), 1226 append: func(b array.Builder, vs interface{}) { b.(*array.Int64Builder).AppendValues(vs.([]int64), valids) }, 1227 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1228 srcvs := src.([]int64) 1229 for i, f := range convertedRec.Column(0).(*array.Float64).Float64Values() { 1230 rawFloat, _ := intToBigFloat(srcvs[i], 5).Float64() 1231 if rawFloat != f { 1232 return i 1233 } 1234 } 1235 return -1 1236 }, 1237 }, 1238 { 1239 logical: "fixed", 1240 physical: "int64", 1241 rowType: execResponseRowType{Scale: 5}, 1242 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1243 values: []int64{12345, 234567}, 1244 withHigherPrecision: true, 1245 nrows: 2, 1246 builder: array.NewInt64Builder(pool), 1247 append: func(b array.Builder, vs interface{}) { b.(*array.Int64Builder).AppendValues(vs.([]int64), valids) }, 1248 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1249 srcvs := src.([]int64) 1250 for i, f := range convertedRec.Column(0).(*array.Int64).Int64Values() { 1251 if srcvs[i] != f { 1252 return i 1253 } 1254 } 1255 return -1 1256 }, 1257 }, 1258 { 1259 logical: "boolean", 1260 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.BooleanType{}}}, nil), 1261 values: []bool{true, false}, 1262 nrows: 2, 1263 builder: array.NewBooleanBuilder(pool), 1264 append: func(b array.Builder, vs interface{}) { b.(*array.BooleanBuilder).AppendValues(vs.([]bool), valids) }, 1265 }, 1266 { 1267 logical: "real", 1268 physical: "float", 1269 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Float64Type{}}}, nil), 1270 values: []float64{1, 2}, 1271 nrows: 2, 1272 builder: array.NewFloat64Builder(pool), 1273 append: func(b array.Builder, vs interface{}) { b.(*array.Float64Builder).AppendValues(vs.([]float64), valids) }, 1274 }, 1275 { 1276 logical: "text", 1277 physical: "string", 1278 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.StringType{}}}, nil), 1279 values: []string{"foo", "bar"}, 1280 nrows: 2, 1281 builder: array.NewStringBuilder(pool), 1282 append: func(b array.Builder, vs interface{}) { b.(*array.StringBuilder).AppendValues(vs.([]string), valids) }, 1283 }, 1284 { 1285 logical: "text", 1286 physical: "string with invalid utf8", 1287 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.StringType{}}}, nil), 1288 rowType: execResponseRowType{Type: "TEXT"}, 1289 values: []string{"\xFF", "bar", "baz\xFF\xFF"}, 1290 expected: []string{"�", "bar", "baz��"}, 1291 enableArrowBatchesUtf8Validation: true, 1292 nrows: 2, 1293 builder: array.NewStringBuilder(pool), 1294 append: func(b array.Builder, vs interface{}) { b.(*array.StringBuilder).AppendValues(vs.([]string), valids) }, 1295 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1296 arr := convertedRec.Column(0).(*array.String) 1297 for i := 0; i < arr.Len(); i++ { 1298 if expected.([]string)[i] != string(arr.Value(i)) { 1299 return i 1300 } 1301 } 1302 return -1 1303 }, 1304 }, 1305 { 1306 logical: "binary", 1307 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.BinaryType{}}}, nil), 1308 values: [][]byte{[]byte("foo"), []byte("bar")}, 1309 nrows: 2, 1310 builder: array.NewBinaryBuilder(pool, arrow.BinaryTypes.Binary), 1311 append: func(b array.Builder, vs interface{}) { b.(*array.BinaryBuilder).AppendValues(vs.([][]byte), valids) }, 1312 }, 1313 { 1314 logical: "date", 1315 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Date32Type{}}}, nil), 1316 values: []time.Time{time.Now(), localTime}, 1317 nrows: 2, 1318 builder: array.NewDate32Builder(pool), 1319 append: func(b array.Builder, vs interface{}) { 1320 for _, d := range vs.([]time.Time) { 1321 b.(*array.Date32Builder).Append(arrow.Date32(d.Unix())) 1322 } 1323 }, 1324 }, 1325 { 1326 logical: "time", 1327 sc: arrow.NewSchema([]arrow.Field{{Type: arrow.FixedWidthTypes.Time64ns}}, nil), 1328 values: []time.Time{time.Now(), time.Now()}, 1329 nrows: 2, 1330 builder: array.NewTime64Builder(pool, arrow.FixedWidthTypes.Time64ns.(*arrow.Time64Type)), 1331 append: func(b array.Builder, vs interface{}) { 1332 for _, t := range vs.([]time.Time) { 1333 b.(*array.Time64Builder).Append(arrow.Time64(t.UnixNano())) 1334 } 1335 }, 1336 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1337 srcvs := src.([]time.Time) 1338 arr := convertedRec.Column(0).(*array.Time64) 1339 for i := 0; i < arr.Len(); i++ { 1340 if srcvs[i].UnixNano() != int64(arr.Value(i)) { 1341 return i 1342 } 1343 } 1344 return -1 1345 }, 1346 }, 1347 { 1348 logical: "timestamp_ntz", 1349 physical: "int64", // timestamp_ntz with scale 0..3 -> int64 1350 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond)}, // Millisecond for scale = 3 1351 nrows: 2, 1352 rowType: execResponseRowType{Scale: 3}, 1353 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1354 builder: array.NewInt64Builder(pool), 1355 append: func(b array.Builder, vs interface{}) { 1356 for _, t := range vs.([]time.Time) { 1357 b.(*array.Int64Builder).Append(t.UnixMilli()) // Millisecond for scale = 3 1358 } 1359 }, 1360 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1361 srcvs := src.([]time.Time) 1362 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1363 if !srcvs[i].Equal(t.ToTime(arrow.Nanosecond)) { 1364 return i 1365 } 1366 } 1367 return -1 1368 }, 1369 }, 1370 { 1371 logical: "timestamp_ntz", 1372 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1373 values: []time.Time{time.Now(), localTime}, 1374 nrows: 2, 1375 rowType: execResponseRowType{Scale: 9}, 1376 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1377 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1378 append: func(b array.Builder, vs interface{}) { 1379 sb := b.(*array.StructBuilder) 1380 valids = []bool{true, true} 1381 sb.AppendValues(valids) 1382 for _, t := range vs.([]time.Time) { 1383 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1384 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1385 } 1386 }, 1387 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1388 srcvs := src.([]time.Time) 1389 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1390 if !srcvs[i].Equal(t.ToTime(arrow.Nanosecond)) { 1391 return i 1392 } 1393 } 1394 return -1 1395 }, 1396 }, 1397 // microsecond timestamp_ntz 1398 { 1399 logical: "timestamp_ntz", 1400 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1401 values: []time.Time{time.Now().Truncate(time.Microsecond), localTime.Truncate(time.Microsecond)}, 1402 arrowBatchesTimestampOption: UseMicrosecondTimestamp, 1403 nrows: 2, 1404 rowType: execResponseRowType{Scale: 9}, 1405 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1406 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1407 append: func(b array.Builder, vs interface{}) { 1408 sb := b.(*array.StructBuilder) 1409 valids = []bool{true, true} 1410 sb.AppendValues(valids) 1411 for _, t := range vs.([]time.Time) { 1412 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1413 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1414 } 1415 }, 1416 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1417 srcvs := src.([]time.Time) 1418 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1419 if !srcvs[i].Equal(t.ToTime(arrow.Microsecond)) { 1420 return i 1421 } 1422 } 1423 return -1 1424 }, 1425 }, 1426 // millisecond timestamp_ntz 1427 { 1428 logical: "timestamp_ntz", 1429 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1430 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond)}, 1431 arrowBatchesTimestampOption: UseMillisecondTimestamp, 1432 nrows: 2, 1433 rowType: execResponseRowType{Scale: 9}, 1434 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1435 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1436 append: func(b array.Builder, vs interface{}) { 1437 sb := b.(*array.StructBuilder) 1438 valids = []bool{true, true} 1439 sb.AppendValues(valids) 1440 for _, t := range vs.([]time.Time) { 1441 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1442 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1443 } 1444 }, 1445 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1446 srcvs := src.([]time.Time) 1447 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1448 if !srcvs[i].Equal(t.ToTime(arrow.Millisecond)) { 1449 return i 1450 } 1451 } 1452 return -1 1453 }, 1454 }, 1455 // second timestamp_ntz 1456 { 1457 logical: "timestamp_ntz", 1458 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1459 values: []time.Time{time.Now().Truncate(time.Second), localTime.Truncate(time.Second)}, 1460 arrowBatchesTimestampOption: UseSecondTimestamp, 1461 nrows: 2, 1462 rowType: execResponseRowType{Scale: 9}, 1463 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1464 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1465 append: func(b array.Builder, vs interface{}) { 1466 sb := b.(*array.StructBuilder) 1467 valids = []bool{true, true} 1468 sb.AppendValues(valids) 1469 for _, t := range vs.([]time.Time) { 1470 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1471 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1472 } 1473 }, 1474 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1475 srcvs := src.([]time.Time) 1476 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1477 if !srcvs[i].Equal(t.ToTime(arrow.Second)) { 1478 return i 1479 } 1480 } 1481 return -1 1482 }, 1483 }, 1484 { 1485 logical: "timestamp_ntz", 1486 physical: "error", 1487 values: []time.Time{localTimeFarIntoFuture}, 1488 error: "Cannot convert timestamp", 1489 nrows: 1, 1490 rowType: execResponseRowType{Scale: 3}, 1491 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1492 builder: array.NewInt64Builder(pool), 1493 append: func(b array.Builder, vs interface{}) { 1494 for _, t := range vs.([]time.Time) { 1495 b.(*array.Int64Builder).Append(t.UnixMilli()) 1496 } 1497 }, 1498 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { return 0 }, 1499 }, 1500 { 1501 logical: "timestamp_ntz", 1502 physical: "int64 with original timestamp", // timestamp_ntz with scale 0..3 -> int64 1503 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond), localTimeFarIntoFuture.Truncate(time.Millisecond)}, 1504 arrowBatchesTimestampOption: UseOriginalTimestamp, 1505 nrows: 3, 1506 rowType: execResponseRowType{Scale: 3}, 1507 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1508 builder: array.NewInt64Builder(pool), 1509 append: func(b array.Builder, vs interface{}) { 1510 for _, t := range vs.([]time.Time) { 1511 b.(*array.Int64Builder).Append(t.UnixMilli()) // Millisecond for scale = 3 1512 } 1513 }, 1514 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1515 srcvs := src.([]time.Time) 1516 for i := 0; i < convertedRec.Column(0).Len(); i++ { 1517 ts := arrowSnowflakeTimestampToTime(convertedRec.Column(0), timestampNtzType, 3, i, nil) 1518 if !srcvs[i].Equal(*ts) { 1519 return i 1520 } 1521 } 1522 return -1 1523 }, 1524 }, 1525 { 1526 logical: "timestamp_ntz", 1527 physical: "struct with original timestamp", // timestamp_ntz with scale 4..9 -> int64 + int32 1528 values: []time.Time{time.Now(), localTime, localTimeFarIntoFuture}, 1529 arrowBatchesTimestampOption: UseOriginalTimestamp, 1530 nrows: 3, 1531 rowType: execResponseRowType{Scale: 9}, 1532 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1533 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1534 append: func(b array.Builder, vs interface{}) { 1535 sb := b.(*array.StructBuilder) 1536 valids = []bool{true, true, true} 1537 sb.AppendValues(valids) 1538 for _, t := range vs.([]time.Time) { 1539 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1540 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1541 } 1542 }, 1543 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1544 srcvs := src.([]time.Time) 1545 for i := 0; i < convertedRec.Column(0).Len(); i++ { 1546 ts := arrowSnowflakeTimestampToTime(convertedRec.Column(0), timestampNtzType, 9, i, nil) 1547 if !srcvs[i].Equal(*ts) { 1548 return i 1549 } 1550 } 1551 return -1 1552 }, 1553 }, 1554 { 1555 logical: "timestamp_ltz", 1556 physical: "int64", // timestamp_ntz with scale 0..3 -> int64 1557 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond)}, 1558 nrows: 2, 1559 rowType: execResponseRowType{Scale: 3}, 1560 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1561 builder: array.NewInt64Builder(pool), 1562 append: func(b array.Builder, vs interface{}) { 1563 for _, t := range vs.([]time.Time) { 1564 b.(*array.Int64Builder).Append(t.UnixMilli()) // Millisecond for scale = 3 1565 } 1566 }, 1567 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1568 srcvs := src.([]time.Time) 1569 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1570 if !srcvs[i].Equal(t.ToTime(arrow.Nanosecond)) { 1571 return i 1572 } 1573 } 1574 return -1 1575 }, 1576 }, 1577 { 1578 logical: "timestamp_ltz", 1579 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1580 values: []time.Time{time.Now(), localTime}, 1581 nrows: 2, 1582 rowType: execResponseRowType{Scale: 9}, 1583 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1584 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1585 append: func(b array.Builder, vs interface{}) { 1586 sb := b.(*array.StructBuilder) 1587 valids = []bool{true, true} 1588 sb.AppendValues(valids) 1589 for _, t := range vs.([]time.Time) { 1590 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1591 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1592 } 1593 }, 1594 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1595 srcvs := src.([]time.Time) 1596 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1597 if !srcvs[i].Equal(t.ToTime(arrow.Nanosecond)) { 1598 return i 1599 } 1600 } 1601 return -1 1602 }, 1603 }, 1604 // microsecond timestamp_ltz 1605 { 1606 logical: "timestamp_ltz", 1607 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1608 values: []time.Time{time.Now().Truncate(time.Microsecond), localTime.Truncate(time.Microsecond)}, 1609 arrowBatchesTimestampOption: UseMicrosecondTimestamp, 1610 nrows: 2, 1611 rowType: execResponseRowType{Scale: 9}, 1612 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1613 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1614 append: func(b array.Builder, vs interface{}) { 1615 sb := b.(*array.StructBuilder) 1616 valids = []bool{true, true} 1617 sb.AppendValues(valids) 1618 for _, t := range vs.([]time.Time) { 1619 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1620 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1621 } 1622 }, 1623 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1624 srcvs := src.([]time.Time) 1625 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1626 if !srcvs[i].Equal(t.ToTime(arrow.Microsecond)) { 1627 return i 1628 } 1629 } 1630 return -1 1631 }, 1632 }, 1633 // millisecond timestamp_ltz 1634 { 1635 logical: "timestamp_ltz", 1636 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1637 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond)}, 1638 arrowBatchesTimestampOption: UseMillisecondTimestamp, 1639 nrows: 2, 1640 rowType: execResponseRowType{Scale: 9}, 1641 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1642 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1643 append: func(b array.Builder, vs interface{}) { 1644 sb := b.(*array.StructBuilder) 1645 valids = []bool{true, true} 1646 sb.AppendValues(valids) 1647 for _, t := range vs.([]time.Time) { 1648 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1649 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1650 } 1651 }, 1652 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1653 srcvs := src.([]time.Time) 1654 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1655 if !srcvs[i].Equal(t.ToTime(arrow.Millisecond)) { 1656 return i 1657 } 1658 } 1659 return -1 1660 }, 1661 }, 1662 // second timestamp_ltz 1663 { 1664 logical: "timestamp_ltz", 1665 physical: "struct", // timestamp_ntz with scale 4..9 -> int64 + int32 1666 values: []time.Time{time.Now().Truncate(time.Second), localTime.Truncate(time.Second)}, 1667 arrowBatchesTimestampOption: UseSecondTimestamp, 1668 nrows: 2, 1669 rowType: execResponseRowType{Scale: 9}, 1670 sc: arrow.NewSchema([]arrow.Field{{Type: timestampNtzStruct}}, nil), 1671 builder: array.NewStructBuilder(pool, timestampNtzStruct), 1672 append: func(b array.Builder, vs interface{}) { 1673 sb := b.(*array.StructBuilder) 1674 valids = []bool{true, true} 1675 sb.AppendValues(valids) 1676 for _, t := range vs.([]time.Time) { 1677 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1678 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1679 } 1680 }, 1681 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1682 srcvs := src.([]time.Time) 1683 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1684 if !srcvs[i].Equal(t.ToTime(arrow.Second)) { 1685 return i 1686 } 1687 } 1688 return -1 1689 }, 1690 }, 1691 { 1692 logical: "timestamp_ltz", 1693 physical: "error", 1694 values: []time.Time{localTimeFarIntoFuture}, 1695 error: "Cannot convert timestamp", 1696 nrows: 1, 1697 rowType: execResponseRowType{Scale: 3}, 1698 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1699 builder: array.NewInt64Builder(pool), 1700 append: func(b array.Builder, vs interface{}) { 1701 for _, t := range vs.([]time.Time) { 1702 b.(*array.Int64Builder).Append(t.UnixMilli()) // Millisecond for scale = 3 1703 } 1704 }, 1705 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { return 0 }, 1706 }, 1707 { 1708 logical: "timestamp_ltz", 1709 physical: "int64 with original timestamp", // timestamp_ntz with scale 0..3 -> int64 1710 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond), localTimeFarIntoFuture.Truncate(time.Millisecond)}, 1711 arrowBatchesTimestampOption: UseOriginalTimestamp, 1712 nrows: 3, 1713 rowType: execResponseRowType{Scale: 3}, 1714 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.Int64Type{}}}, nil), 1715 builder: array.NewInt64Builder(pool), 1716 append: func(b array.Builder, vs interface{}) { 1717 for _, t := range vs.([]time.Time) { 1718 b.(*array.Int64Builder).Append(t.UnixMilli()) // Millisecond for scale = 3 1719 } 1720 }, 1721 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1722 srcvs := src.([]time.Time) 1723 for i := 0; i < convertedRec.Column(0).Len(); i++ { 1724 ts := arrowSnowflakeTimestampToTime(convertedRec.Column(0), timestampLtzType, 3, i, localTime.Location()) 1725 if !srcvs[i].Equal(*ts) { 1726 return i 1727 } 1728 } 1729 return -1 1730 }, 1731 }, 1732 { 1733 logical: "timestamp_ltz", 1734 physical: "struct with original timestamp", // timestamp_ntz with scale 4..9 -> int64 + int32 1735 values: []time.Time{time.Now(), localTime, localTimeFarIntoFuture}, 1736 arrowBatchesTimestampOption: UseOriginalTimestamp, 1737 nrows: 3, 1738 rowType: execResponseRowType{Scale: 9}, 1739 sc: arrow.NewSchema([]arrow.Field{{Type: timestampLtzStruct}}, nil), 1740 builder: array.NewStructBuilder(pool, timestampLtzStruct), 1741 append: func(b array.Builder, vs interface{}) { 1742 sb := b.(*array.StructBuilder) 1743 valids = []bool{true, true, true} 1744 sb.AppendValues(valids) 1745 for _, t := range vs.([]time.Time) { 1746 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1747 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1748 } 1749 }, 1750 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1751 srcvs := src.([]time.Time) 1752 for i := 0; i < convertedRec.Column(0).Len(); i++ { 1753 ts := arrowSnowflakeTimestampToTime(convertedRec.Column(0), timestampLtzType, 9, i, localTime.Location()) 1754 if !srcvs[i].Equal(*ts) { 1755 return i 1756 } 1757 } 1758 return -1 1759 }, 1760 }, 1761 { 1762 logical: "timestamp_tz", 1763 physical: "struct2", // timestamp_tz with scale 0..3 -> int64 + int32 1764 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond)}, 1765 nrows: 2, 1766 rowType: execResponseRowType{Scale: 3}, 1767 sc: arrow.NewSchema([]arrow.Field{{Type: timestampTzStructWithoutFraction}}, nil), 1768 builder: array.NewStructBuilder(pool, timestampTzStructWithoutFraction), 1769 append: func(b array.Builder, vs interface{}) { 1770 sb := b.(*array.StructBuilder) 1771 valids = []bool{true, true} 1772 sb.AppendValues(valids) 1773 for _, t := range vs.([]time.Time) { 1774 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.UnixMilli()) // Millisecond for scale = 3 1775 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(0)) // timezone index - not important in tests 1776 } 1777 }, 1778 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1779 srcvs := src.([]time.Time) 1780 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1781 if !srcvs[i].Equal(t.ToTime(arrow.Nanosecond)) { 1782 return i 1783 } 1784 } 1785 return -1 1786 }, 1787 }, 1788 { 1789 logical: "timestamp_tz", 1790 physical: "struct3", // timestamp_tz with scale 4..9 -> int64 + int32 + int32 1791 values: []time.Time{time.Now(), localTime}, 1792 nrows: 2, 1793 rowType: execResponseRowType{Scale: 9}, 1794 sc: arrow.NewSchema([]arrow.Field{{Type: timestampTzStructWithFraction}}, nil), 1795 builder: array.NewStructBuilder(pool, timestampTzStructWithFraction), 1796 append: func(b array.Builder, vs interface{}) { 1797 sb := b.(*array.StructBuilder) 1798 valids = []bool{true, true} 1799 sb.AppendValues(valids) 1800 for _, t := range vs.([]time.Time) { 1801 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1802 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1803 sb.FieldBuilder(2).(*array.Int32Builder).Append(int32(0)) // timezone index - not important in tests 1804 } 1805 }, 1806 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1807 srcvs := src.([]time.Time) 1808 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1809 if !srcvs[i].Equal(t.ToTime(arrow.Nanosecond)) { 1810 return i 1811 } 1812 } 1813 return -1 1814 }, 1815 }, 1816 // microsecond timestamp_tz 1817 { 1818 logical: "timestamp_tz", 1819 physical: "struct3", // timestamp_tz with scale 4..9 -> int64 + int32 + int32 1820 values: []time.Time{time.Now().Truncate(time.Microsecond), localTime.Truncate(time.Microsecond)}, 1821 arrowBatchesTimestampOption: UseMicrosecondTimestamp, 1822 nrows: 2, 1823 rowType: execResponseRowType{Scale: 9}, 1824 sc: arrow.NewSchema([]arrow.Field{{Type: timestampTzStructWithFraction}}, nil), 1825 builder: array.NewStructBuilder(pool, timestampTzStructWithFraction), 1826 append: func(b array.Builder, vs interface{}) { 1827 sb := b.(*array.StructBuilder) 1828 valids = []bool{true, true} 1829 sb.AppendValues(valids) 1830 for _, t := range vs.([]time.Time) { 1831 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1832 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1833 sb.FieldBuilder(2).(*array.Int32Builder).Append(int32(0)) // timezone index - not important in tests 1834 } 1835 }, 1836 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1837 srcvs := src.([]time.Time) 1838 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1839 if !srcvs[i].Equal(t.ToTime(arrow.Microsecond)) { 1840 return i 1841 } 1842 } 1843 return -1 1844 }, 1845 }, 1846 // millisecond timestamp_tz 1847 { 1848 logical: "timestamp_tz", 1849 physical: "struct3", // timestamp_tz with scale 4..9 -> int64 + int32 + int32 1850 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond)}, 1851 arrowBatchesTimestampOption: UseMillisecondTimestamp, 1852 nrows: 2, 1853 rowType: execResponseRowType{Scale: 9}, 1854 sc: arrow.NewSchema([]arrow.Field{{Type: timestampTzStructWithFraction}}, nil), 1855 builder: array.NewStructBuilder(pool, timestampTzStructWithFraction), 1856 append: func(b array.Builder, vs interface{}) { 1857 sb := b.(*array.StructBuilder) 1858 valids = []bool{true, true} 1859 sb.AppendValues(valids) 1860 for _, t := range vs.([]time.Time) { 1861 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1862 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1863 sb.FieldBuilder(2).(*array.Int32Builder).Append(int32(0)) // timezone index - not important in tests 1864 } 1865 }, 1866 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1867 srcvs := src.([]time.Time) 1868 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1869 if !srcvs[i].Equal(t.ToTime(arrow.Millisecond)) { 1870 return i 1871 } 1872 } 1873 return -1 1874 }, 1875 }, 1876 // second timestamp_tz 1877 { 1878 logical: "timestamp_tz", 1879 physical: "struct3", // timestamp_tz with scale 4..9 -> int64 + int32 + int32 1880 values: []time.Time{time.Now().Truncate(time.Second), localTime.Truncate(time.Second)}, 1881 arrowBatchesTimestampOption: UseSecondTimestamp, 1882 nrows: 2, 1883 rowType: execResponseRowType{Scale: 9}, 1884 sc: arrow.NewSchema([]arrow.Field{{Type: timestampTzStructWithFraction}}, nil), 1885 builder: array.NewStructBuilder(pool, timestampTzStructWithFraction), 1886 append: func(b array.Builder, vs interface{}) { 1887 sb := b.(*array.StructBuilder) 1888 valids = []bool{true, true} 1889 sb.AppendValues(valids) 1890 for _, t := range vs.([]time.Time) { 1891 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1892 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1893 sb.FieldBuilder(2).(*array.Int32Builder).Append(int32(0)) // timezone index - not important in tests 1894 } 1895 }, 1896 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1897 srcvs := src.([]time.Time) 1898 for i, t := range convertedRec.Column(0).(*array.Timestamp).TimestampValues() { 1899 if !srcvs[i].Equal(t.ToTime(arrow.Second)) { 1900 return i 1901 } 1902 } 1903 return -1 1904 }, 1905 }, 1906 { 1907 logical: "timestamp_tz", 1908 physical: "struct2 with original timestamp", // timestamp_ntz with scale 0..3 -> int64 + int32 1909 values: []time.Time{time.Now().Truncate(time.Millisecond), localTime.Truncate(time.Millisecond), localTimeFarIntoFuture.Truncate(time.Millisecond)}, 1910 arrowBatchesTimestampOption: UseOriginalTimestamp, 1911 nrows: 3, 1912 rowType: execResponseRowType{Scale: 3}, 1913 sc: arrow.NewSchema([]arrow.Field{{Type: timestampTzStructWithoutFraction}}, nil), 1914 builder: array.NewStructBuilder(pool, timestampTzStructWithoutFraction), 1915 append: func(b array.Builder, vs interface{}) { 1916 sb := b.(*array.StructBuilder) 1917 valids = []bool{true, true, true} 1918 sb.AppendValues(valids) 1919 for _, t := range vs.([]time.Time) { 1920 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.UnixMilli()) // Millisecond for scale = 3 1921 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(0)) // timezone index - not important in tests 1922 } 1923 }, 1924 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1925 srcvs := src.([]time.Time) 1926 for i := 0; i < convertedRec.Column(0).Len(); i++ { 1927 ts := arrowSnowflakeTimestampToTime(convertedRec.Column(0), timestampTzType, 3, i, nil) 1928 if !srcvs[i].Equal(*ts) { 1929 return i 1930 } 1931 } 1932 return -1 1933 }, 1934 }, 1935 { 1936 logical: "timestamp_tz", 1937 physical: "struct3 with original timestamp", // timestamp_ntz with scale 4..9 -> int64 + int32 + int32 1938 values: []time.Time{time.Now(), localTime, localTimeFarIntoFuture}, 1939 arrowBatchesTimestampOption: UseOriginalTimestamp, 1940 nrows: 3, 1941 rowType: execResponseRowType{Scale: 9}, 1942 sc: arrow.NewSchema([]arrow.Field{{Type: timestampTzStructWithFraction}}, nil), 1943 builder: array.NewStructBuilder(pool, timestampTzStructWithFraction), 1944 append: func(b array.Builder, vs interface{}) { 1945 sb := b.(*array.StructBuilder) 1946 valids = []bool{true, true, true} 1947 sb.AppendValues(valids) 1948 for _, t := range vs.([]time.Time) { 1949 sb.FieldBuilder(0).(*array.Int64Builder).Append(t.Unix()) 1950 sb.FieldBuilder(1).(*array.Int32Builder).Append(int32(t.Nanosecond())) 1951 sb.FieldBuilder(2).(*array.Int32Builder).Append(int32(0)) // timezone index - not important in tests 1952 } 1953 }, 1954 compare: func(src interface{}, expected interface{}, convertedRec arrow.Record) int { 1955 srcvs := src.([]time.Time) 1956 for i := 0; i < convertedRec.Column(0).Len(); i++ { 1957 ts := arrowSnowflakeTimestampToTime(convertedRec.Column(0), timestampTzType, 9, i, nil) 1958 if !srcvs[i].Equal(*ts) { 1959 return i 1960 } 1961 } 1962 return -1 1963 }, 1964 }, 1965 { 1966 logical: "array", 1967 values: [][]string{{"foo", "bar"}, {"baz", "quz", "quux"}}, 1968 nrows: 2, 1969 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.StringType{}}}, nil), 1970 builder: array.NewStringBuilder(pool), 1971 append: func(b array.Builder, vs interface{}) { 1972 for _, a := range vs.([][]string) { 1973 b.(*array.StringBuilder).Append(fmt.Sprint(a)) 1974 } 1975 }, 1976 }, 1977 { 1978 logical: "object", 1979 values: []testObj{{0, "foo"}, {1, "bar"}}, 1980 nrows: 2, 1981 sc: arrow.NewSchema([]arrow.Field{{Type: &arrow.StringType{}}}, nil), 1982 builder: array.NewStringBuilder(pool), 1983 append: func(b array.Builder, vs interface{}) { 1984 for _, o := range vs.([]testObj) { 1985 b.(*array.StringBuilder).Append(fmt.Sprint(o)) 1986 } 1987 }, 1988 }, 1989 } { 1990 testName := tc.logical 1991 if tc.physical != "" { 1992 testName += " " + tc.physical 1993 } 1994 t.Run(testName, func(t *testing.T) { 1995 scope := memory.NewCheckedAllocatorScope(pool) 1996 defer scope.CheckSize(t) 1997 1998 b := tc.builder 1999 defer b.Release() 2000 tc.append(b, tc.values) 2001 arr := b.NewArray() 2002 defer arr.Release() 2003 rawRec := array.NewRecord(tc.sc, []arrow.Array{arr}, int64(tc.nrows)) 2004 defer rawRec.Release() 2005 2006 meta := tc.rowType 2007 meta.Type = tc.logical 2008 2009 ctx := context.Background() 2010 switch tc.arrowBatchesTimestampOption { 2011 case UseOriginalTimestamp: 2012 ctx = WithArrowBatchesTimestampOption(ctx, UseOriginalTimestamp) 2013 case UseSecondTimestamp: 2014 ctx = WithArrowBatchesTimestampOption(ctx, UseSecondTimestamp) 2015 case UseMillisecondTimestamp: 2016 ctx = WithArrowBatchesTimestampOption(ctx, UseMillisecondTimestamp) 2017 case UseMicrosecondTimestamp: 2018 ctx = WithArrowBatchesTimestampOption(ctx, UseMicrosecondTimestamp) 2019 default: 2020 ctx = WithArrowBatchesTimestampOption(ctx, UseNanosecondTimestamp) 2021 } 2022 2023 if tc.enableArrowBatchesUtf8Validation { 2024 ctx = WithArrowBatchesUtf8Validation(ctx) 2025 } 2026 2027 if tc.withHigherPrecision { 2028 ctx = WithHigherPrecision(ctx) 2029 } 2030 2031 transformedRec, err := arrowToRecord(ctx, rawRec, pool, []execResponseRowType{meta}, localTime.Location()) 2032 if err != nil { 2033 if tc.error == "" || !strings.Contains(err.Error(), tc.error) { 2034 t.Fatalf("error: %s", err) 2035 } 2036 } else { 2037 defer transformedRec.Release() 2038 if tc.error != "" { 2039 t.Fatalf("expected error: %s", tc.error) 2040 } 2041 2042 if tc.compare != nil { 2043 idx := tc.compare(tc.values, tc.expected, transformedRec) 2044 if idx != -1 { 2045 t.Fatalf("error: column array value mismatch at index %v", idx) 2046 } 2047 } else { 2048 for i, c := range transformedRec.Columns() { 2049 rawCol := rawRec.Column(i) 2050 if rawCol != c { 2051 t.Fatalf("error: expected column %s, got column %s", rawCol, c) 2052 } 2053 } 2054 } 2055 } 2056 }) 2057 } 2058 } 2059 2060 func TestTimestampLTZLocation(t *testing.T) { 2061 runSnowflakeConnTest(t, func(sct *SCTest) { 2062 src := "1549491451.123456789" 2063 var dest driver.Value 2064 loc, _ := time.LoadLocation(PSTLocation) 2065 if err := stringToValue(&dest, execResponseRowType{Type: "timestamp_ltz"}, &src, loc); err != nil { 2066 t.Errorf("unexpected error: %v", err) 2067 } 2068 ts, ok := dest.(time.Time) 2069 if !ok { 2070 t.Errorf("expected type: 'time.Time', got '%v'", reflect.TypeOf(dest)) 2071 } 2072 if ts.Location() != loc { 2073 t.Errorf("expected location to be %v, got '%v'", loc, ts.Location()) 2074 } 2075 2076 if err := stringToValue(&dest, execResponseRowType{Type: "timestamp_ltz"}, &src, nil); err != nil { 2077 t.Errorf("unexpected error: %v", err) 2078 } 2079 ts, ok = dest.(time.Time) 2080 if !ok { 2081 t.Errorf("expected type: 'time.Time', got '%v'", reflect.TypeOf(dest)) 2082 } 2083 if ts.Location() != time.Local { 2084 t.Errorf("expected location to be local, got '%v'", ts.Location()) 2085 } 2086 }) 2087 } 2088 2089 func TestSmallTimestampBinding(t *testing.T) { 2090 runSnowflakeConnTest(t, func(sct *SCTest) { 2091 ctx := context.Background() 2092 timeValue, err := time.Parse("2006-01-02 15:04:05", "1600-10-10 10:10:10") 2093 if err != nil { 2094 t.Fatalf("failed to parse time: %v", err) 2095 } 2096 parameters := []driver.NamedValue{ 2097 {Ordinal: 1, Value: DataTypeTimestampNtz}, 2098 {Ordinal: 2, Value: timeValue}, 2099 } 2100 2101 rows := sct.mustQueryContext(ctx, "SELECT ?", parameters) 2102 defer rows.Close() 2103 2104 scanValues := make([]driver.Value, 1) 2105 for { 2106 if err := rows.Next(scanValues); err == io.EOF { 2107 break 2108 } else if err != nil { 2109 t.Fatalf("failed to run query: %v", err) 2110 } 2111 if scanValues[0] != timeValue { 2112 t.Fatalf("unexpected result. expected: %v, got: %v", timeValue, scanValues[0]) 2113 } 2114 } 2115 }) 2116 } 2117 2118 func TestTimestampConversionWithoutArrowBatches(t *testing.T) { 2119 timestamps := [3]string{ 2120 "2000-10-10 10:10:10.123456789", // neutral 2121 "9999-12-12 23:59:59.999999999", // max 2122 "0001-01-01 00:00:00.000000000"} // min 2123 types := [3]string{"TIMESTAMP_NTZ", "TIMESTAMP_LTZ", "TIMESTAMP_TZ"} 2124 2125 runDBTest(t, func(sct *DBTest) { 2126 ctx := context.Background() 2127 2128 for _, tsStr := range timestamps { 2129 ts, err := time.Parse("2006-01-02 15:04:05", tsStr) 2130 if err != nil { 2131 t.Fatalf("failed to parse time: %v", err) 2132 } 2133 for _, tp := range types { 2134 for scale := 0; scale <= 9; scale++ { 2135 t.Run(tp+"("+strconv.Itoa(scale)+")_"+tsStr, func(t *testing.T) { 2136 query := fmt.Sprintf("SELECT '%s'::%s(%v)", tsStr, tp, scale) 2137 rows := sct.mustQueryContext(ctx, query, nil) 2138 defer rows.Close() 2139 2140 if rows.Next() { 2141 var act time.Time 2142 rows.Scan(&act) 2143 exp := ts.Truncate(time.Duration(math.Pow10(9 - scale))) 2144 if !exp.Equal(act) { 2145 t.Fatalf("unexpected result. expected: %v, got: %v", exp, act) 2146 } 2147 } else { 2148 t.Fatalf("failed to run query: %v", query) 2149 } 2150 }) 2151 } 2152 } 2153 } 2154 }) 2155 } 2156 2157 func TestTimestampConversionWithArrowBatchesNanosecondFailsForDistantDates(t *testing.T) { 2158 timestamps := [2]string{ 2159 "9999-12-12 23:59:59.999999999", // max 2160 "0001-01-01 00:00:00.000000000"} // min 2161 types := [3]string{"TIMESTAMP_NTZ", "TIMESTAMP_LTZ", "TIMESTAMP_TZ"} 2162 2163 expectedError := "Cannot convert timestamp" 2164 2165 runSnowflakeConnTest(t, func(sct *SCTest) { 2166 ctx := WithArrowBatches(sct.sc.ctx) 2167 2168 pool := memory.NewCheckedAllocator(memory.DefaultAllocator) 2169 defer pool.AssertSize(t, 0) 2170 ctx = WithArrowAllocator(ctx, pool) 2171 2172 for _, tsStr := range timestamps { 2173 for _, tp := range types { 2174 for scale := 0; scale <= 9; scale++ { 2175 t.Run(tp+"("+strconv.Itoa(scale)+")_"+tsStr, func(t *testing.T) { 2176 2177 query := fmt.Sprintf("SELECT '%s'::%s(%v)", tsStr, tp, scale) 2178 _, err := sct.sc.QueryContext(ctx, query, []driver.NamedValue{}) 2179 if err != nil { 2180 if !strings.Contains(err.Error(), expectedError) { 2181 t.Fatalf("improper error, expected: %v, got: %v", expectedError, err.Error()) 2182 } 2183 } else { 2184 t.Fatalf("no error, expected: %v ", expectedError) 2185 2186 } 2187 }) 2188 } 2189 } 2190 } 2191 }) 2192 } 2193 2194 // use arrow.Timestamp with microsecond precision and below should not encounter overflow issue. 2195 func TestTimestampConversionWithArrowBatchesMicrosecondPassesForDistantDates(t *testing.T) { 2196 timestamps := [2]string{ 2197 "9999-12-12 23:59:59.999999999", // max 2198 "0001-01-01 00:00:00.000000000"} // min 2199 types := [3]string{"TIMESTAMP_NTZ", "TIMESTAMP_LTZ", "TIMESTAMP_TZ"} 2200 2201 runSnowflakeConnTest(t, func(sct *SCTest) { 2202 ctx := WithArrowBatchesTimestampOption(WithArrowBatches(sct.sc.ctx), UseMicrosecondTimestamp) 2203 2204 pool := memory.NewCheckedAllocator(memory.DefaultAllocator) 2205 defer pool.AssertSize(t, 0) 2206 ctx = WithArrowAllocator(ctx, pool) 2207 2208 for _, tsStr := range timestamps { 2209 for _, tp := range types { 2210 for scale := 0; scale <= 9; scale++ { 2211 t.Run(tp+"("+strconv.Itoa(scale)+")_"+tsStr, func(t *testing.T) { 2212 2213 query := fmt.Sprintf("SELECT '%s'::%s(%v)", tsStr, tp, scale) 2214 rows, err := sct.sc.QueryContext(ctx, query, []driver.NamedValue{}) 2215 if err != nil { 2216 t.Fatalf("failed to query: %v", err) 2217 } 2218 defer rows.Close() 2219 2220 // getting result batches 2221 batches, err := rows.(*snowflakeRows).GetArrowBatches() 2222 if err != nil { 2223 t.Error(err) 2224 } 2225 2226 rec, err := batches[0].Fetch() 2227 if err != nil { 2228 t.Error(err) 2229 } 2230 2231 records := *rec 2232 r := records[0] 2233 defer r.Release() 2234 actual := r.Column(0).(*array.Timestamp).TimestampValues()[0] 2235 actualYear := actual.ToTime(arrow.Microsecond).Year() 2236 2237 ts, err := time.Parse("2006-01-02 15:04:05", tsStr) 2238 if err != nil { 2239 t.Fatalf("failed to parse time: %v", err) 2240 } 2241 exp := ts.Truncate(time.Duration(math.Pow10(9 - scale))) 2242 2243 if actualYear != exp.Year() { 2244 t.Fatalf("unexpected year in timestamp, expected: %v, got: %v", exp.Year(), actualYear) 2245 } 2246 }) 2247 } 2248 } 2249 } 2250 }) 2251 } 2252 2253 func TestTimestampConversionWithArrowBatchesAndWithOriginalTimestamp(t *testing.T) { 2254 timestamps := [3]string{ 2255 "2000-10-10 10:10:10.123456789", // neutral 2256 "9999-12-12 23:59:59.999999999", // max 2257 "0001-01-01 00:00:00.000000000"} // min 2258 types := [3]string{"TIMESTAMP_NTZ", "TIMESTAMP_LTZ", "TIMESTAMP_TZ"} 2259 2260 runSnowflakeConnTest(t, func(sct *SCTest) { 2261 ctx := WithArrowBatchesTimestampOption(WithArrowBatches(sct.sc.ctx), UseOriginalTimestamp) 2262 pool := memory.NewCheckedAllocator(memory.DefaultAllocator) 2263 defer pool.AssertSize(t, 0) 2264 ctx = WithArrowAllocator(ctx, pool) 2265 2266 for _, tsStr := range timestamps { 2267 ts, err := time.Parse("2006-01-02 15:04:05", tsStr) 2268 if err != nil { 2269 t.Fatalf("failed to parse time: %v", err) 2270 } 2271 for _, tp := range types { 2272 for scale := 0; scale <= 9; scale++ { 2273 t.Run(tp+"("+strconv.Itoa(scale)+")_"+tsStr, func(t *testing.T) { 2274 2275 query := fmt.Sprintf("SELECT '%s'::%s(%v)", tsStr, tp, scale) 2276 rows := sct.mustQueryContext(ctx, query, []driver.NamedValue{}) 2277 defer rows.Close() 2278 2279 // getting result batches 2280 batches, err := rows.(*snowflakeRows).GetArrowBatches() 2281 if err != nil { 2282 t.Error(err) 2283 } 2284 2285 numBatches := len(batches) 2286 if numBatches != 1 { 2287 t.Errorf("incorrect number of batches, expected: 1, got: %v", numBatches) 2288 } 2289 2290 rec, err := batches[0].Fetch() 2291 if err != nil { 2292 t.Error(err) 2293 } 2294 exp := ts.Truncate(time.Duration(math.Pow10(9 - scale))) 2295 for _, r := range *rec { 2296 defer r.Release() 2297 act := batches[0].ArrowSnowflakeTimestampToTime(r, 0, 0) 2298 if act == nil { 2299 t.Fatalf("unexpected result. expected: %v, got: nil", exp) 2300 } else if !exp.Equal(*act) { 2301 t.Fatalf("unexpected result. expected: %v, got: %v", exp, act) 2302 } 2303 } 2304 }) 2305 } 2306 } 2307 } 2308 }) 2309 } 2310 2311 func TestTimeTypeValueToString(t *testing.T) { 2312 timeValue, err := time.Parse("2006-01-02 15:04:05", "2020-01-02 10:11:12") 2313 if err != nil { 2314 t.Fatal(err) 2315 } 2316 offsetTimeValue, err := time.ParseInLocation("2006-01-02 15:04:05", "2020-01-02 10:11:12", Location(6*60)) 2317 if err != nil { 2318 t.Fatal(err) 2319 } 2320 2321 testcases := []struct { 2322 in time.Time 2323 tsmode snowflakeType 2324 out string 2325 }{ 2326 {timeValue, dateType, "1577959872000"}, 2327 {timeValue, timeType, "36672000000000"}, 2328 {timeValue, timestampNtzType, "1577959872000000000"}, 2329 {timeValue, timestampLtzType, "1577959872000000000"}, 2330 {timeValue, timestampTzType, "1577959872000000000 1440"}, 2331 {offsetTimeValue, timestampTzType, "1577938272000000000 1800"}, 2332 } 2333 2334 for _, tc := range testcases { 2335 t.Run(tc.out, func(t *testing.T) { 2336 output, err := timeTypeValueToString(tc.in, tc.tsmode) 2337 if err != nil { 2338 t.Error(err) 2339 } 2340 if strings.Compare(tc.out, *output) != 0 { 2341 t.Errorf("failed to convert time %v of type %v. expected: %v, received: %v", tc.in, tc.tsmode, tc.out, *output) 2342 } 2343 }) 2344 } 2345 }