feat(bigquery): add support for bignumeric (#2779)
feat(bigquery): add support for bignumeric
This PR adds basic support for the BIGNUMERIC type in BigQuery.
This library has several cases where it tries to infer the appropriate BigQuery type from a native Go type. For big.Rat types, we continue the existing behavior of mapping them to NUMERIC, as the big.Rat doesn't
provide a general way of indicating desired precision or scale to determine whether BIGNUMERIC is a more
appropriate mapping.
diff --git a/bigquery/integration_test.go b/bigquery/integration_test.go
index 14573e2..60959d0 100644
--- a/bigquery/integration_test.go
+++ b/bigquery/integration_test.go
@@ -1434,6 +1434,7 @@
ctm := civil.Time{Hour: 15, Minute: 4, Second: 5, Nanosecond: 6000}
cdt := civil.DateTime{Date: testDate, Time: ctm}
rat := big.NewRat(33, 100)
+ rat2 := big.NewRat(66, 100)
geo := "POINT(-122.198939 47.669865)"
// Nil fields in the struct.
@@ -1455,20 +1456,21 @@
// Populate the struct with values.
testInsertAndReadNullable(t, testStructNullable{
- String: NullString{"x", true},
- Bytes: []byte{1, 2, 3},
- Integer: NullInt64{1, true},
- Float: NullFloat64{2.3, true},
- Boolean: NullBool{true, true},
- Timestamp: NullTimestamp{testTimestamp, true},
- Date: NullDate{testDate, true},
- Time: NullTime{ctm, true},
- DateTime: NullDateTime{cdt, true},
- Numeric: rat,
- Geography: NullGeography{geo, true},
- Record: &subNullable{X: NullInt64{4, true}},
+ String: NullString{"x", true},
+ Bytes: []byte{1, 2, 3},
+ Integer: NullInt64{1, true},
+ Float: NullFloat64{2.3, true},
+ Boolean: NullBool{true, true},
+ Timestamp: NullTimestamp{testTimestamp, true},
+ Date: NullDate{testDate, true},
+ Time: NullTime{ctm, true},
+ DateTime: NullDateTime{cdt, true},
+ Numeric: rat,
+ BigNumeric: rat2,
+ Geography: NullGeography{geo, true},
+ Record: &subNullable{X: NullInt64{4, true}},
},
- []Value{"x", []byte{1, 2, 3}, int64(1), 2.3, true, testTimestamp, testDate, ctm, cdt, rat, geo, []Value{int64(4)}})
+ []Value{"x", []byte{1, 2, 3}, int64(1), 2.3, true, testTimestamp, testDate, ctm, cdt, rat, rat2, geo, []Value{int64(4)}})
}
func testInsertAndReadNullable(t *testing.T, ts testStructNullable, wantRow []Value) {
diff --git a/bigquery/params.go b/bigquery/params.go
index 5957022..68bb3fa 100644
--- a/bigquery/params.go
+++ b/bigquery/params.go
@@ -65,16 +65,17 @@
var fieldCache = fields.NewCache(bqTagParser, nil, nil)
var (
- int64ParamType = &bq.QueryParameterType{Type: "INT64"}
- float64ParamType = &bq.QueryParameterType{Type: "FLOAT64"}
- boolParamType = &bq.QueryParameterType{Type: "BOOL"}
- stringParamType = &bq.QueryParameterType{Type: "STRING"}
- bytesParamType = &bq.QueryParameterType{Type: "BYTES"}
- dateParamType = &bq.QueryParameterType{Type: "DATE"}
- timeParamType = &bq.QueryParameterType{Type: "TIME"}
- dateTimeParamType = &bq.QueryParameterType{Type: "DATETIME"}
- timestampParamType = &bq.QueryParameterType{Type: "TIMESTAMP"}
- numericParamType = &bq.QueryParameterType{Type: "NUMERIC"}
+ int64ParamType = &bq.QueryParameterType{Type: "INT64"}
+ float64ParamType = &bq.QueryParameterType{Type: "FLOAT64"}
+ boolParamType = &bq.QueryParameterType{Type: "BOOL"}
+ stringParamType = &bq.QueryParameterType{Type: "STRING"}
+ bytesParamType = &bq.QueryParameterType{Type: "BYTES"}
+ dateParamType = &bq.QueryParameterType{Type: "DATE"}
+ timeParamType = &bq.QueryParameterType{Type: "TIME"}
+ dateTimeParamType = &bq.QueryParameterType{Type: "DATETIME"}
+ timestampParamType = &bq.QueryParameterType{Type: "TIMESTAMP"}
+ numericParamType = &bq.QueryParameterType{Type: "NUMERIC"}
+ bigNumericParamType = &bq.QueryParameterType{Type: "BIGNUMERIC"}
)
var (
@@ -233,6 +234,9 @@
return res, nil
case typeOfRat:
+ // big.Rat types don't communicate scale or precision, so we cannot
+ // disambiguate between NUMERIC and BIGNUMERIC. For now, we'll continue
+ // to honor previous behavior and send as Numeric type.
res.Value = NumericString(v.Interface().(*big.Rat))
return res, nil
}
@@ -304,14 +308,15 @@
}
var paramTypeToFieldType = map[string]FieldType{
- int64ParamType.Type: IntegerFieldType,
- float64ParamType.Type: FloatFieldType,
- boolParamType.Type: BooleanFieldType,
- stringParamType.Type: StringFieldType,
- bytesParamType.Type: BytesFieldType,
- dateParamType.Type: DateFieldType,
- timeParamType.Type: TimeFieldType,
- numericParamType.Type: NumericFieldType,
+ int64ParamType.Type: IntegerFieldType,
+ float64ParamType.Type: FloatFieldType,
+ boolParamType.Type: BooleanFieldType,
+ stringParamType.Type: StringFieldType,
+ bytesParamType.Type: BytesFieldType,
+ dateParamType.Type: DateFieldType,
+ timeParamType.Type: TimeFieldType,
+ numericParamType.Type: NumericFieldType,
+ bigNumericParamType.Type: BigNumericFieldType,
}
// Convert a parameter value from the service to a Go value. This is similar to, but
diff --git a/bigquery/schema.go b/bigquery/schema.go
index a9a50ef..dcb5c6f 100644
--- a/bigquery/schema.go
+++ b/bigquery/schema.go
@@ -182,23 +182,27 @@
// GeographyFieldType is a string field type. Geography types represent a set of points
// on the Earth's surface, represented in Well Known Text (WKT) format.
GeographyFieldType FieldType = "GEOGRAPHY"
+ // BigNumericFieldType is a numeric field type that supports values of larger precision
+ // and scale than the NumericFieldType.
+ BigNumericFieldType FieldType = "BIGNUMERIC"
)
var (
errEmptyJSONSchema = errors.New("bigquery: empty JSON schema")
fieldTypes = map[FieldType]bool{
- StringFieldType: true,
- BytesFieldType: true,
- IntegerFieldType: true,
- FloatFieldType: true,
- BooleanFieldType: true,
- TimestampFieldType: true,
- RecordFieldType: true,
- DateFieldType: true,
- TimeFieldType: true,
- DateTimeFieldType: true,
- NumericFieldType: true,
- GeographyFieldType: true,
+ StringFieldType: true,
+ BytesFieldType: true,
+ IntegerFieldType: true,
+ FloatFieldType: true,
+ BooleanFieldType: true,
+ TimestampFieldType: true,
+ RecordFieldType: true,
+ DateFieldType: true,
+ TimeFieldType: true,
+ DateTimeFieldType: true,
+ NumericFieldType: true,
+ GeographyFieldType: true,
+ BigNumericFieldType: true,
}
// The API will accept alias names for the types based on the Standard SQL type names.
fieldAliases = map[FieldType]FieldType{
@@ -346,6 +350,10 @@
case typeOfDateTime:
return &FieldSchema{Required: true, Type: DateTimeFieldType}, nil
case typeOfRat:
+ // We automatically infer big.Rat values as NUMERIC as we cannot
+ // determine precision/scale from the type. Users who want the
+ // larger precision of BIGNUMERIC need to manipulate the inferred
+ // schema.
return &FieldSchema{Required: !nullable, Type: NumericFieldType}, nil
}
if ft := nullableFieldType(rt); ft != "" {
diff --git a/bigquery/schema_test.go b/bigquery/schema_test.go
index 5bd1084..ae93698 100644
--- a/bigquery/schema_test.go
+++ b/bigquery/schema_test.go
@@ -1041,7 +1041,8 @@
{"name":"flat_date","type":"DATE","mode":"NULLABLE","description":"Flat required DATE"},
{"name":"flat_time","type":"TIME","mode":"REQUIRED","description":"Flat nullable TIME"},
{"name":"flat_datetime","type":"DATETIME","mode":"NULLABLE","description":"Flat required DATETIME"},
- {"name":"flat_numeric","type":"NUMERIC","mode":"REQUIRED","description":"Flat nullable NUMERIC"},
+ {"name":"flat_numeric","type":"NUMERIC","mode":"REQUIRED","description":"Flat required NUMERIC"},
+ {"name":"flat_bignumeric","type":"BIGNUMERIC","mode":"NULLABLE","description":"Flat nullable BIGNUMERIC"},
{"name":"flat_geography","type":"GEOGRAPHY","mode":"REQUIRED","description":"Flat required GEOGRAPHY"},
{"name":"aliased_integer","type":"INT64","mode":"REQUIRED","description":"Aliased required integer"},
{"name":"aliased_boolean","type":"BOOL","mode":"NULLABLE","description":"Aliased nullable boolean"},
@@ -1058,7 +1059,8 @@
fieldSchema("Flat required DATE", "flat_date", "DATE", false, false, nil),
fieldSchema("Flat nullable TIME", "flat_time", "TIME", false, true, nil),
fieldSchema("Flat required DATETIME", "flat_datetime", "DATETIME", false, false, nil),
- fieldSchema("Flat nullable NUMERIC", "flat_numeric", "NUMERIC", false, true, nil),
+ fieldSchema("Flat required NUMERIC", "flat_numeric", "NUMERIC", false, true, nil),
+ fieldSchema("Flat nullable BIGNUMERIC", "flat_bignumeric", "BIGNUMERIC", false, false, nil),
fieldSchema("Flat required GEOGRAPHY", "flat_geography", "GEOGRAPHY", false, true, nil),
fieldSchema("Aliased required integer", "aliased_integer", "INTEGER", false, true, nil),
fieldSchema("Aliased nullable boolean", "aliased_boolean", "BOOLEAN", false, false, nil),
diff --git a/bigquery/value.go b/bigquery/value.go
index 361341c..cae28be 100644
--- a/bigquery/value.go
+++ b/bigquery/value.go
@@ -407,6 +407,13 @@
return setNull(v, x, func() interface{} { return x.(*big.Rat) })
}
}
+
+ case BigNumericFieldType:
+ if ftype == typeOfRat {
+ return func(v reflect.Value, x interface{}) error {
+ return setNull(v, x, func() interface{} { return x.(*big.Rat) })
+ }
+ }
}
return nil
}
@@ -692,7 +699,7 @@
}
func toUploadValue(val interface{}, fs *FieldSchema) interface{} {
- if fs.Type == TimeFieldType || fs.Type == DateTimeFieldType || fs.Type == NumericFieldType {
+ if fs.Type == TimeFieldType || fs.Type == DateTimeFieldType || fs.Type == NumericFieldType || fs.Type == BigNumericFieldType {
return toUploadValueReflect(reflect.ValueOf(val), fs)
}
return val
@@ -721,6 +728,13 @@
return formatUploadValue(v, fs, func(v reflect.Value) string {
return NumericString(v.Interface().(*big.Rat))
})
+ case BigNumericFieldType:
+ if r, ok := v.Interface().(*big.Rat); ok && r == nil {
+ return nil
+ }
+ return formatUploadValue(v, fs, func(v reflect.Value) string {
+ return BigNumericString(v.Interface().(*big.Rat))
+ })
default:
if !fs.Repeated || v.Len() > 0 {
return v.Interface()
@@ -786,6 +800,12 @@
// NumericScaleDigits is the maximum number of digits after the decimal point in a NUMERIC value.
NumericScaleDigits = 9
+
+ // BigNumericPrecisionDigits is the maximum number of full digits in a BIGNUMERIC value.
+ BigNumericPrecisionDigits = 76
+
+ // BigNumericScaleDigits is the maximum number of full digits in a BIGNUMERIC value.
+ BigNumericScaleDigits = 38
)
// NumericString returns a string representing a *big.Rat in a format compatible
@@ -795,6 +815,12 @@
return r.FloatString(NumericScaleDigits)
}
+// BigNumericString returns a string representing a *big.Rat in a format compatible with BigQuery
+// SQL. It returns a floating point literal with 38 digits after the decimal point.
+func BigNumericString(r *big.Rat) string {
+ return r.FloatString(BigNumericScaleDigits)
+}
+
// convertRows converts a series of TableRows into a series of Value slices.
// schema is used to interpret the data from rows; its length must match the
// length of each row.
@@ -913,6 +939,12 @@
return nil, fmt.Errorf("bigquery: invalid NUMERIC value %q", val)
}
return Value(r), nil
+ case BigNumericFieldType:
+ r, ok := (&big.Rat{}).SetString(val)
+ if !ok {
+ return nil, fmt.Errorf("bigquery: invalid BIGNUMERIC value %q", val)
+ }
+ return Value(r), nil
case GeographyFieldType:
return val, nil
default:
diff --git a/bigquery/value_test.go b/bigquery/value_test.go
index 1e5bb2d..4d08336 100644
--- a/bigquery/value_test.go
+++ b/bigquery/value_test.go
@@ -36,6 +36,7 @@
{Type: BooleanFieldType},
{Type: BytesFieldType},
{Type: NumericFieldType},
+ {Type: BigNumericFieldType},
{Type: GeographyFieldType},
}
row := &bq.TableRow{
@@ -46,6 +47,7 @@
{V: "true"},
{V: base64.StdEncoding.EncodeToString([]byte("foo"))},
{V: "123.123456789"},
+ {V: "99999999999999999999999999999999999999.99999999999999999999999999999999999999"},
{V: testGeography},
},
}
@@ -54,7 +56,9 @@
t.Fatalf("error converting: %v", err)
}
- want := []Value{"a", int64(1), 1.2, true, []byte("foo"), big.NewRat(123123456789, 1e9), testGeography}
+ bigRatVal := new(big.Rat)
+ bigRatVal.SetString("99999999999999999999999999999999999999.99999999999999999999999999999999999999")
+ want := []Value{"a", int64(1), 1.2, true, []byte("foo"), big.NewRat(123123456789, 1e9), bigRatVal, testGeography}
if !testutil.Equal(got, want) {
t.Errorf("converting basic values: got:\n%v\nwant:\n%v", got, want)
}
@@ -444,6 +448,7 @@
{Name: "strField", Type: StringFieldType},
{Name: "dtField", Type: DateTimeFieldType},
{Name: "nField", Type: NumericFieldType},
+ {Name: "bigNumField", Type: BigNumericFieldType},
{Name: "geoField", Type: GeographyFieldType},
},
InsertID: "iid",
@@ -452,16 +457,18 @@
Date: civil.Date{Year: 1, Month: 2, Day: 3},
Time: civil.Time{Hour: 4, Minute: 5, Second: 6, Nanosecond: 7000}},
big.NewRat(123456789000, 1e9),
+ big.NewRat(1, 3),
testGeography,
},
},
wantInsertID: "iid",
wantRow: map[string]Value{
- "intField": 1,
- "strField": "a",
- "dtField": "0001-02-03 04:05:06.000007",
- "nField": "123.456789000",
- "geoField": testGeography,
+ "intField": 1,
+ "strField": "a",
+ "dtField": "0001-02-03 04:05:06.000007",
+ "nField": "123.456789000",
+ "bigNumField": "0.33333333333333333333333333333333333333",
+ "geoField": testGeography,
},
},
{
@@ -603,6 +610,8 @@
{Name: "p", Type: IntegerFieldType, Required: false},
{Name: "n", Type: NumericFieldType, Required: false},
{Name: "nr", Type: NumericFieldType, Repeated: true},
+ {Name: "bn", Type: BigNumericFieldType, Required: false},
+ {Name: "bnr", Type: BigNumericFieldType, Repeated: true},
{Name: "g", Type: GeographyFieldType, Required: false},
{Name: "gr", Type: GeographyFieldType, Repeated: true},
}
@@ -619,6 +628,8 @@
P NullInt64
N *big.Rat
NR []*big.Rat
+ BN *big.Rat
+ BNR []*big.Rat
G NullGeography
GR []string // Repeated Geography
}
@@ -654,6 +665,8 @@
P: NullInt64{Valid: true, Int64: 17},
N: big.NewRat(123456, 1000),
NR: []*big.Rat{big.NewRat(3, 1), big.NewRat(56789, 1e5)},
+ BN: big.NewRat(1, 3),
+ BNR: []*big.Rat{big.NewRat(1, 3), big.NewRat(1, 2)},
G: NullGeography{Valid: true, GeographyVal: "POINT(-122.350220 47.649154)"},
GR: []string{"POINT(-122.350220 47.649154)", "POINT(-122.198939 47.669865)"},
}
@@ -667,6 +680,8 @@
"p": NullInt64{Valid: true, Int64: 17},
"n": "123.456000000",
"nr": []string{"3.000000000", "0.567890000"},
+ "bn": "0.33333333333333333333333333333333333333",
+ "bnr": []string{"0.33333333333333333333333333333333333333", "0.50000000000000000000000000000000000000"},
"g": NullGeography{Valid: true, GeographyVal: "POINT(-122.350220 47.649154)"},
"gr": []string{"POINT(-122.350220 47.649154)", "POINT(-122.198939 47.669865)"},
}
@@ -729,20 +744,24 @@
}
}
-func TestNumericString(t *testing.T) {
+func TestNumericStrings(t *testing.T) {
for _, test := range []struct {
- in *big.Rat
- want string
+ description string
+ in *big.Rat
+ wantNumeric string
+ wantBigNumeric string
}{
- {big.NewRat(2, 3), "0.666666667"}, // round to 9 places
- {big.NewRat(1, 2), "0.500000000"},
- {big.NewRat(1, 2*1e8), "0.000000005"},
- {big.NewRat(5, 1e10), "0.000000001"}, // round up the 5 in the 10th decimal place
- {big.NewRat(-5, 1e10), "-0.000000001"}, // round half away from zero
+ {"repeating with rounding", big.NewRat(2, 3), "0.666666667", "0.66666666666666666666666666666666666667"},
+ {"all zero padding", big.NewRat(1, 2), "0.500000000", "0.50000000000000000000000000000000000000"},
+ {"zero pad with digit", big.NewRat(1, 2*1e8), "0.000000005", "0.00000000500000000000000000000000000000"},
+ {"smaller rounding case 1", big.NewRat(5, 1e10), "0.000000001", "0.00000000050000000000000000000000000000"},
+ {"smaller rounding case 2", big.NewRat(-5, 1e10), "-0.000000001", "-0.00000000050000000000000000000000000000"},
} {
- got := NumericString(test.in)
- if got != test.want {
- t.Errorf("%v: got %q, want %q", test.in, got, test.want)
+ if got := NumericString(test.in); got != test.wantNumeric {
+ t.Errorf("case %q, val %v as numeric: got %q, want %q", test.description, test.in, got, test.wantNumeric)
+ }
+ if got := BigNumericString(test.in); got != test.wantBigNumeric {
+ t.Errorf("case %q, val %v as bignumeric: got %q, want %q", test.description, test.in, got, test.wantBigNumeric)
}
}
}
@@ -887,6 +906,7 @@
{Name: "T", Type: TimeFieldType},
{Name: "DT", Type: DateTimeFieldType},
{Name: "N", Type: NumericFieldType},
+ {Name: "BN", Type: BigNumericFieldType},
{Name: "G", Type: GeographyFieldType},
{Name: "nested", Type: RecordFieldType, Schema: Schema{
{Name: "nestS", Type: StringFieldType},
@@ -895,16 +915,17 @@
{Name: "t", Type: StringFieldType},
}
- testTimestamp = time.Date(2016, 11, 5, 7, 50, 22, 8, time.UTC)
- testDate = civil.Date{Year: 2016, Month: 11, Day: 5}
- testTime = civil.Time{Hour: 7, Minute: 50, Second: 22, Nanosecond: 8}
- testDateTime = civil.DateTime{Date: testDate, Time: testTime}
- testNumeric = big.NewRat(123, 456)
+ testTimestamp = time.Date(2016, 11, 5, 7, 50, 22, 8, time.UTC)
+ testDate = civil.Date{Year: 2016, Month: 11, Day: 5}
+ testTime = civil.Time{Hour: 7, Minute: 50, Second: 22, Nanosecond: 8}
+ testDateTime = civil.DateTime{Date: testDate, Time: testTime}
+ testNumeric = big.NewRat(123, 456)
+ testBigNumeric = big.NewRat(456, 789)
// testGeography is a WKT string representing a single point.
testGeography = "POINT(-122.350220 47.649154)"
testValues = []Value{"x", "y", []byte{1, 2, 3}, int64(7), int64(8), 3.14, true,
- testTimestamp, testDate, testTime, testDateTime, testNumeric, testGeography,
+ testTimestamp, testDate, testTime, testDateTime, testNumeric, testBigNumeric, testGeography,
[]Value{"nested", int64(17)}, "z"}
)
@@ -918,6 +939,7 @@
By []byte
F float64
N *big.Rat
+ BN *big.Rat
G string
Nested nested
Tagged string `bigquery:"t"`
@@ -952,6 +974,7 @@
S2: "y",
By: []byte{1, 2, 3},
N: big.NewRat(123, 456),
+ BN: big.NewRat(456, 789),
G: testGeography,
Nested: nested{NestS: "nested", NestI: 17},
Tagged: "z",
@@ -1043,18 +1066,19 @@
}
type testStructNullable struct {
- String NullString
- Bytes []byte
- Integer NullInt64
- Float NullFloat64
- Boolean NullBool
- Timestamp NullTimestamp
- Date NullDate
- Time NullTime
- DateTime NullDateTime
- Numeric *big.Rat
- Geography NullGeography
- Record *subNullable
+ String NullString
+ Bytes []byte
+ Integer NullInt64
+ Float NullFloat64
+ Boolean NullBool
+ Timestamp NullTimestamp
+ Date NullDate
+ Time NullTime
+ DateTime NullDateTime
+ Numeric *big.Rat
+ BigNumeric *big.Rat
+ Geography NullGeography
+ Record *subNullable
}
type subNullable struct {
@@ -1072,6 +1096,7 @@
{Name: "Time", Type: TimeFieldType, Required: false},
{Name: "DateTime", Type: DateTimeFieldType, Required: false},
{Name: "Numeric", Type: NumericFieldType, Required: false},
+ {Name: "BigNumeric", Type: BigNumericFieldType, Required: false},
{Name: "Geography", Type: GeographyFieldType, Required: false},
{Name: "Record", Type: RecordFieldType, Required: false, Schema: Schema{
{Name: "X", Type: IntegerFieldType, Required: false},
@@ -1088,24 +1113,25 @@
}
nonnilVals := []Value{"x", []byte{1, 2, 3}, int64(1), 2.3, true, testTimestamp, testDate, testTime,
- testDateTime, big.NewRat(1, 2), testGeography, []Value{int64(4)}}
+ testDateTime, big.NewRat(1, 2), big.NewRat(3, 4), testGeography, []Value{int64(4)}}
// All ts fields are nil. Loading non-nil values will cause them all to
// be allocated.
mustLoad(t, &ts, testStructNullableSchema, nonnilVals)
want = testStructNullable{
- String: NullString{StringVal: "x", Valid: true},
- Bytes: []byte{1, 2, 3},
- Integer: NullInt64{Int64: 1, Valid: true},
- Float: NullFloat64{Float64: 2.3, Valid: true},
- Boolean: NullBool{Bool: true, Valid: true},
- Timestamp: NullTimestamp{Timestamp: testTimestamp, Valid: true},
- Date: NullDate{Date: testDate, Valid: true},
- Time: NullTime{Time: testTime, Valid: true},
- DateTime: NullDateTime{DateTime: testDateTime, Valid: true},
- Numeric: big.NewRat(1, 2),
- Geography: NullGeography{GeographyVal: testGeography, Valid: true},
- Record: &subNullable{X: NullInt64{Int64: 4, Valid: true}},
+ String: NullString{StringVal: "x", Valid: true},
+ Bytes: []byte{1, 2, 3},
+ Integer: NullInt64{Int64: 1, Valid: true},
+ Float: NullFloat64{Float64: 2.3, Valid: true},
+ Boolean: NullBool{Bool: true, Valid: true},
+ Timestamp: NullTimestamp{Timestamp: testTimestamp, Valid: true},
+ Date: NullDate{Date: testDate, Valid: true},
+ Time: NullTime{Time: testTime, Valid: true},
+ DateTime: NullDateTime{DateTime: testDateTime, Valid: true},
+ Numeric: big.NewRat(1, 2),
+ BigNumeric: big.NewRat(3, 4),
+ Geography: NullGeography{GeographyVal: testGeography, Valid: true},
+ Record: &subNullable{X: NullInt64{Int64: 4, Valid: true}},
}
if diff := testutil.Diff(ts, want); diff != "" {
t.Error(diff)
@@ -1114,7 +1140,7 @@
// Struct pointers are reused, byte slices are not.
want = ts
want.Bytes = []byte{17}
- vals2 := []Value{nil, []byte{17}, nil, nil, nil, nil, nil, nil, nil, nil, nil, []Value{int64(7)}}
+ vals2 := []Value{nil, []byte{17}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []Value{int64(7)}}
mustLoad(t, &ts, testStructNullableSchema, vals2)
if ts.Record != want.Record {
t.Error("record pointers not identical")
diff --git a/go.sum b/go.sum
index ea567ca..b75391c 100644
--- a/go.sum
+++ b/go.sum
@@ -22,6 +22,7 @@
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0 h1:a/O/bK/vWrYGOTFtH8di4rBxMZnmkjy+Y5LxpDwo+dA=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
@@ -348,6 +349,7 @@
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=