Skip to content

Commit 1a9e204

Browse files
authored
feat(bigquery): add rounding mode to FieldSchema (#10328)
1 parent d5150d3 commit 1a9e204

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed

bigquery/schema.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ type FieldSchema struct {
152152
// Information about the range.
153153
// If the type is RANGE, this field is required.
154154
RangeElementType *RangeElementType
155+
156+
// RoundingMode specifies the rounding mode to be used when storing
157+
// values of NUMERIC and BIGNUMERIC type.
158+
// If unspecified, default value is RoundHalfAwayFromZero.
159+
RoundingMode RoundingMode
155160
}
156161

157162
func (fs *FieldSchema) toBQ() *bq.TableFieldSchema {
@@ -166,6 +171,7 @@ func (fs *FieldSchema) toBQ() *bq.TableFieldSchema {
166171
DefaultValueExpression: fs.DefaultValueExpression,
167172
Collation: string(fs.Collation),
168173
RangeElementType: fs.RangeElementType.toBQ(),
174+
RoundingMode: string(fs.RoundingMode),
169175
}
170176

171177
if fs.Repeated {
@@ -253,6 +259,7 @@ func bqToFieldSchema(tfs *bq.TableFieldSchema) *FieldSchema {
253259
DefaultValueExpression: tfs.DefaultValueExpression,
254260
Collation: tfs.Collation,
255261
RangeElementType: bqToRangeElementType(tfs.RangeElementType),
262+
RoundingMode: RoundingMode(tfs.RoundingMode),
256263
}
257264

258265
for _, f := range tfs.Fields {
@@ -431,6 +438,21 @@ func InferSchema(st interface{}) (Schema, error) {
431438
return inferSchemaReflectCached(reflect.TypeOf(st))
432439
}
433440

441+
// RoundingMode represents the rounding mode to be used when storing
442+
// values of NUMERIC and BIGNUMERIC type.
443+
type RoundingMode string
444+
445+
const (
446+
// RoundHalfAwayFromZero rounds half values away from zero when applying
447+
// precision and scale upon writing of NUMERIC and BIGNUMERIC values.
448+
// For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2
449+
RoundHalfAwayFromZero RoundingMode = "ROUND_HALF_AWAY_FROM_ZERO"
450+
// RoundHalfEven rounds half values to the nearest even value when applying
451+
// precision and scale upon writing of NUMERIC and BIGNUMERIC values.
452+
// For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5 => 2 1.6, 1.7, 1.8, 1.9 => 2 2.5 => 2
453+
RoundHalfEven RoundingMode = "ROUND_HALF_EVEN"
454+
)
455+
434456
var schemaCache sync.Map
435457

436458
type cacheVal struct {

bigquery/schema_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,34 @@ func TestSchemaConversion(t *testing.T) {
376376
},
377377
},
378378
},
379+
{
380+
// rounding mode values
381+
bqSchema: &bq.TableSchema{
382+
Fields: []*bq.TableFieldSchema{
383+
{
384+
Name: "num",
385+
Type: "NUMERIC",
386+
RoundingMode: "ROUND_HALF_AWAY_FROM_ZERO",
387+
},
388+
{
389+
Name: "bignum",
390+
Type: "BIGNUMERIC",
391+
RoundingMode: "ROUND_HALF_EVEN",
392+
},
393+
}},
394+
schema: Schema{
395+
{
396+
Name: "num",
397+
Type: NumericFieldType,
398+
RoundingMode: RoundHalfAwayFromZero,
399+
},
400+
{
401+
Name: "bignum",
402+
Type: BigNumericFieldType,
403+
RoundingMode: RoundHalfEven,
404+
},
405+
},
406+
},
379407
{
380408
// policy tags
381409
bqSchema: &bq.TableSchema{

bigquery/table_integration_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,66 @@ func TestIntegration_TableDefaultCollation(t *testing.T) {
696696
}
697697
}
698698

699+
func TestIntegration_TableRoundingMode(t *testing.T) {
700+
// Test RoundingMode for Table.Create and Table.Update
701+
if client == nil {
702+
t.Skip("Integration tests skipped")
703+
}
704+
ctx := context.Background()
705+
table := dataset.Table(tableIDs.New())
706+
schema := Schema{
707+
{Name: "num", Type: NumericFieldType, RoundingMode: RoundHalfAwayFromZero},
708+
}
709+
err := table.Create(context.Background(), &TableMetadata{
710+
Schema: schema,
711+
ExpirationTime: testTableExpiration,
712+
})
713+
if err != nil {
714+
t.Fatal(err)
715+
}
716+
defer table.Delete(ctx)
717+
md, err := table.Metadata(ctx)
718+
if err != nil {
719+
t.Fatal(err)
720+
}
721+
722+
// all fields should be RoundHalfAwayFromZero
723+
for _, field := range md.Schema {
724+
if field.RoundingMode != RoundHalfAwayFromZero {
725+
t.Fatalf("expected to have rounding mode %q, but found %q on field: %v", RoundHalfAwayFromZero, field.RoundingMode, field.Name)
726+
}
727+
}
728+
729+
// Add a bignum field with different rounding mode
730+
updatedSchema := md.Schema
731+
updatedSchema = append(updatedSchema, &FieldSchema{
732+
Name: "bignum",
733+
Type: BigNumericFieldType,
734+
RoundingMode: RoundHalfEven,
735+
})
736+
md, err = table.Update(ctx, TableMetadataToUpdate{
737+
Schema: updatedSchema,
738+
}, "")
739+
if err != nil {
740+
t.Fatal(err)
741+
}
742+
743+
// numeric field is RoundHalfAwayFromZero
744+
// and bignum is RoundHalfEven
745+
for _, field := range md.Schema {
746+
if field.Type == NumericFieldType {
747+
if field.RoundingMode != RoundHalfAwayFromZero {
748+
t.Fatalf("expected to have rounding mode %q, but found %q on field: %v", RoundHalfAwayFromZero, field.RoundingMode, field.Name)
749+
}
750+
}
751+
if field.Type == BigNumericFieldType {
752+
if field.RoundingMode != RoundHalfEven {
753+
t.Fatalf("expected to have rounding mode %q, but found %q on field: %v", RoundHalfEven, field.RoundingMode, field.Name)
754+
}
755+
}
756+
}
757+
}
758+
699759
func TestIntegration_TableConstraintsPK(t *testing.T) {
700760
// Test Primary Keys for Table.Create and Table.Update
701761
if client == nil {

0 commit comments

Comments
 (0)