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  }