Skip to content

Commit

Permalink
feat(bigquery): add rounding mode to FieldSchema (googleapis#10328)
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarowolfx committed Jun 5, 2024
1 parent d5150d3 commit 1a9e204
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
22 changes: 22 additions & 0 deletions bigquery/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ type FieldSchema struct {
// Information about the range.
// If the type is RANGE, this field is required.
RangeElementType *RangeElementType

// RoundingMode specifies the rounding mode to be used when storing
// values of NUMERIC and BIGNUMERIC type.
// If unspecified, default value is RoundHalfAwayFromZero.
RoundingMode RoundingMode
}

func (fs *FieldSchema) toBQ() *bq.TableFieldSchema {
Expand All @@ -166,6 +171,7 @@ func (fs *FieldSchema) toBQ() *bq.TableFieldSchema {
DefaultValueExpression: fs.DefaultValueExpression,
Collation: string(fs.Collation),
RangeElementType: fs.RangeElementType.toBQ(),
RoundingMode: string(fs.RoundingMode),
}

if fs.Repeated {
Expand Down Expand Up @@ -253,6 +259,7 @@ func bqToFieldSchema(tfs *bq.TableFieldSchema) *FieldSchema {
DefaultValueExpression: tfs.DefaultValueExpression,
Collation: tfs.Collation,
RangeElementType: bqToRangeElementType(tfs.RangeElementType),
RoundingMode: RoundingMode(tfs.RoundingMode),
}

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

// RoundingMode represents the rounding mode to be used when storing
// values of NUMERIC and BIGNUMERIC type.
type RoundingMode string

const (
// RoundHalfAwayFromZero rounds half values away from zero when applying
// precision and scale upon writing of NUMERIC and BIGNUMERIC values.
// For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2
RoundHalfAwayFromZero RoundingMode = "ROUND_HALF_AWAY_FROM_ZERO"
// RoundHalfEven rounds half values to the nearest even value when applying
// precision and scale upon writing of NUMERIC and BIGNUMERIC values.
// 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
RoundHalfEven RoundingMode = "ROUND_HALF_EVEN"
)

var schemaCache sync.Map

type cacheVal struct {
Expand Down
28 changes: 28 additions & 0 deletions bigquery/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,34 @@ func TestSchemaConversion(t *testing.T) {
},
},
},
{
// rounding mode values
bqSchema: &bq.TableSchema{
Fields: []*bq.TableFieldSchema{
{
Name: "num",
Type: "NUMERIC",
RoundingMode: "ROUND_HALF_AWAY_FROM_ZERO",
},
{
Name: "bignum",
Type: "BIGNUMERIC",
RoundingMode: "ROUND_HALF_EVEN",
},
}},
schema: Schema{
{
Name: "num",
Type: NumericFieldType,
RoundingMode: RoundHalfAwayFromZero,
},
{
Name: "bignum",
Type: BigNumericFieldType,
RoundingMode: RoundHalfEven,
},
},
},
{
// policy tags
bqSchema: &bq.TableSchema{
Expand Down
60 changes: 60 additions & 0 deletions bigquery/table_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,66 @@ func TestIntegration_TableDefaultCollation(t *testing.T) {
}
}

func TestIntegration_TableRoundingMode(t *testing.T) {
// Test RoundingMode for Table.Create and Table.Update
if client == nil {
t.Skip("Integration tests skipped")
}
ctx := context.Background()
table := dataset.Table(tableIDs.New())
schema := Schema{
{Name: "num", Type: NumericFieldType, RoundingMode: RoundHalfAwayFromZero},
}
err := table.Create(context.Background(), &TableMetadata{
Schema: schema,
ExpirationTime: testTableExpiration,
})
if err != nil {
t.Fatal(err)
}
defer table.Delete(ctx)
md, err := table.Metadata(ctx)
if err != nil {
t.Fatal(err)
}

// all fields should be RoundHalfAwayFromZero
for _, field := range md.Schema {
if field.RoundingMode != RoundHalfAwayFromZero {
t.Fatalf("expected to have rounding mode %q, but found %q on field: %v", RoundHalfAwayFromZero, field.RoundingMode, field.Name)
}
}

// Add a bignum field with different rounding mode
updatedSchema := md.Schema
updatedSchema = append(updatedSchema, &FieldSchema{
Name: "bignum",
Type: BigNumericFieldType,
RoundingMode: RoundHalfEven,
})
md, err = table.Update(ctx, TableMetadataToUpdate{
Schema: updatedSchema,
}, "")
if err != nil {
t.Fatal(err)
}

// numeric field is RoundHalfAwayFromZero
// and bignum is RoundHalfEven
for _, field := range md.Schema {
if field.Type == NumericFieldType {
if field.RoundingMode != RoundHalfAwayFromZero {
t.Fatalf("expected to have rounding mode %q, but found %q on field: %v", RoundHalfAwayFromZero, field.RoundingMode, field.Name)
}
}
if field.Type == BigNumericFieldType {
if field.RoundingMode != RoundHalfEven {
t.Fatalf("expected to have rounding mode %q, but found %q on field: %v", RoundHalfEven, field.RoundingMode, field.Name)
}
}
}
}

func TestIntegration_TableConstraintsPK(t *testing.T) {
// Test Primary Keys for Table.Create and Table.Update
if client == nil {
Expand Down

0 comments on commit 1a9e204

Please sign in to comment.