Repository: googleapis/google-cloud-go
Pull request #4103 (2021-05-13 22:05:25)

feat(bigquery): add support for parameterized types
Exposes type parameterization constraints for
STRING, BYTES, NUMERIC, BIGNUMERIC types.


Patch:
diff --git a/bigquery/schema.go b/bigquery/schema.go
index 0bcd85a606..9b117e40a7 100644
--- a/bigquery/schema.go
+++ b/bigquery/schema.go
@@ -70,6 +70,41 @@ type FieldSchema struct {
 
 	// Describes the nested schema if Type is set to Record.
 	Schema Schema
+
+	// Maximum length of the field for STRING or BYTES type.
+	//
+	// It is invalid to set value for types other than STRING or BYTES.
+	//
+	// For STRING type, this represents the maximum UTF-8 length of strings
+	// allowed in the field. For BYTES type, this represents the maximum
+	// number of bytes in the field.
+	MaxLength int64
+
+	// Precision can be used to constrain the maximum number of
+	// total digits allowed for NUMERIC or BIGNUMERIC types.
+	//
+	// It is invalid to set values for Precision for types other than
+	// NUMERIC or BIGNUMERIC.
+	//
+	// For NUMERIC type, acceptable values for Precision must
+	// be: 1 ≤ (Precision - Scale) ≤ 29. Values for Scale
+	// must be: 0 ≤ Scale ≤ 9.
+	//
+	// For BIGNUMERIC type, acceptable values for Precision must
+	// be: 1 ≤ (Precision - Scale) ≤ 38. Values for Scale
+	// must be: 0 ≤ Scale ≤ 38.
+	Precision int64
+
+	// Scale can be used to constrain the maximum number of digits
+	// in the fractional part of a NUMERIC or BIGNUMERIC type.
+	//
+	// If the Scale value is set, the Precision value must be set as well.
+	//
+	// It is invalid to set values for Scale for types other than
+	// NUMERIC or BIGNUMERIC.
+	//
+	// See the Precision field for additional guidance about valid values.
+	Scale int64
 }
 
 func (fs *FieldSchema) toBQ() *bq.TableFieldSchema {
@@ -78,6 +113,9 @@ func (fs *FieldSchema) toBQ() *bq.TableFieldSchema {
 		Name:        fs.Name,
 		Type:        string(fs.Type),
 		PolicyTags:  fs.PolicyTags.toBQ(),
+		MaxLength:   fs.MaxLength,
+		Precision:   fs.Precision,
+		Scale:       fs.Scale,
 	}
 
 	if fs.Repeated {
@@ -133,6 +171,9 @@ func bqToFieldSchema(tfs *bq.TableFieldSchema) *FieldSchema {
 		Required:    tfs.Mode == "REQUIRED",
 		Type:        FieldType(tfs.Type),
 		PolicyTags:  bqToPolicyTagList(tfs.PolicyTags),
+		MaxLength:   tfs.MaxLength,
+		Precision:   tfs.Precision,
+		Scale:       tfs.Scale,
 	}
 
 	for _, f := range tfs.Fields {


Test patch:
diff --git a/bigquery/integration_test.go b/bigquery/integration_test.go
index cedd4fafbb..4206a44bc6 100644
--- a/bigquery/integration_test.go
+++ b/bigquery/integration_test.go
@@ -249,6 +249,36 @@ func TestIntegration_TableCreate(t *testing.T) {
 	}
 }
 
+func TestIntegration_TableCreateWithConstraints(t *testing.T) {
+	if client == nil {
+		t.Skip("Integration tests skipped")
+	}
+	table := dataset.Table("constraints")
+	schema := Schema{
+		{Name: "str_col", Type: StringFieldType, MaxLength: 10},
+		{Name: "bytes_col", Type: BytesFieldType, MaxLength: 150},
+		{Name: "num_col", Type: NumericFieldType, Precision: 20},
+		{Name: "bignumeric_col", Type: BigNumericFieldType, Precision: 30, Scale: 5},
+	}
+	err := table.Create(context.Background(), &TableMetadata{
+		Schema:         schema,
+		ExpirationTime: testTableExpiration.Add(5 * time.Minute),
+	})
+	if err != nil {
+		t.Fatalf("table create error: %v", err)
+	}
+
+	meta, err := table.Metadata(context.Background())
+	if err != nil {
+		t.Fatalf("couldn't get metadata: %v", err)
+	}
+
+	if diff := testutil.Diff(meta.Schema, schema); diff != "" {
+		t.Fatalf("got=-, want=+:\n%s", diff)
+	}
+
+}
+
 func TestIntegration_TableCreateView(t *testing.T) {
 	if client == nil {
 		t.Skip("Integration tests skipped")
diff --git a/bigquery/schema_test.go b/bigquery/schema_test.go
index 6bdbf13858..f877e36766 100644
--- a/bigquery/schema_test.go
+++ b/bigquery/schema_test.go
@@ -284,6 +284,40 @@ func TestSchemaConversion(t *testing.T) {
 				fieldSchema("geo", "g", "GEOGRAPHY", false, false, nil),
 			},
 		},
+		{
+			// constrained
+			bqSchema: &bq.TableSchema{
+				Fields: []*bq.TableFieldSchema{
+					{
+						Name:      "foo",
+						Type:      "STRING",
+						MaxLength: 0,
+						Precision: 0,
+						Scale:     0,
+					},
+					{
+						Name:      "bar",
+						Type:      "STRING",
+						MaxLength: 1,
+						Precision: 2,
+						Scale:     3,
+					},
+				}},
+			schema: Schema{
+				{Name: "foo",
+					Type:      StringFieldType,
+					MaxLength: 0,
+					Precision: 0,
+					Scale:     0,
+				},
+				{Name: "bar",
+					Type:      StringFieldType,
+					MaxLength: 1,
+					Precision: 2,
+					Scale:     3,
+				},
+			},
+		},
 		{
 			// policy tags
 			bqSchema: &bq.TableSchema{