Skip to content

Add foreign type info and definition to google_bigquery_table beta #9122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/12659.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
bigquery: added `schema_foreign_type_info` field and related schema handling to `google_bigquery_table` resource (beta)
```
63 changes: 61 additions & 2 deletions google-beta/services/bigquery/resource_bigquery_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ func jsonCompareWithMapKeyOverride(key string, a, b interface{}, compareMapKeyVa
for subKey := range objectB {
unionOfKeys[subKey] = true
}

// Disregard "type" and "fields" if "foreignTypeDefinition" is present since they may have been modified by the server.
if _, ok := unionOfKeys["foreignTypeDefinition"]; ok {
delete(unionOfKeys, "type")
delete(unionOfKeys, "fields")
}
for subKey := range unionOfKeys {
eq := compareMapKeyVal(subKey, objectA, objectB)
if !eq {
Expand Down Expand Up @@ -325,6 +329,11 @@ func resourceBigQueryTableSchemaIsChangeable(old, new interface{}, isExternalTab
for key := range objectNew {
unionOfKeys[key] = true
}
// Disregard "type" and "fields" if "foreignTypeDefinition" is present since they may have been modified by the server.
if _, ok := unionOfKeys["foreignTypeDefinition"]; ok {
delete(unionOfKeys, "type")
delete(unionOfKeys, "fields")
}
for key := range unionOfKeys {
valOld := objectOld[key]
valNew := objectNew[key]
Expand Down Expand Up @@ -994,6 +1003,24 @@ func ResourceBigQueryTable() *schema.Resource {
DiffSuppressFunc: bigQueryTableSchemaDiffSuppress,
Description: `A JSON schema for the table.`,
},
// SchemaForeignTypeInfo: [Optional] Specifies metadata of the foreign data type definition in field schema.
"schema_foreign_type_info": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
MaxItems: 1,
Description: "Specifies metadata of the foreign data type definition in field schema.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
// TypeSystem: [Required] Specifies the system which defines the foreign data type.
"type_system": {
Type: schema.TypeString,
Required: true,
Description: `Specifies the system which defines the foreign data type.`,
},
},
},
},
// View: [Optional] If specified, configures this table as a view.
"view": {
Type: schema.TypeList,
Expand Down Expand Up @@ -1626,7 +1653,11 @@ func resourceTable(d *schema.ResourceData, meta interface{}) (*bigquery.Table, e
}
table.Schema = schema
}

if v, ok := d.GetOk("schema_foreign_type_info"); ok {
if table.Schema != nil {
table.Schema.ForeignTypeInfo = expandForeignTypeInfo(v)
}
}
if v, ok := d.GetOk("time_partitioning"); ok {
table.TimePartitioning = expandTimePartitioning(v)
}
Expand Down Expand Up @@ -1926,6 +1957,12 @@ func resourceBigQueryTableRead(d *schema.ResourceData, meta interface{}) error {
if err := d.Set("schema", schema); err != nil {
return fmt.Errorf("Error setting schema: %s", err)
}
if res.Schema.ForeignTypeInfo != nil {
foreignTypeInfo := flattenForeignTypeInfo(res.Schema.ForeignTypeInfo)
if err := d.Set("schema_foreign_type_info", foreignTypeInfo); err != nil {
return fmt.Errorf("Error setting schema_foreign_type_info: %s", err)
}
}
}

if res.View != nil {
Expand Down Expand Up @@ -2724,7 +2761,29 @@ func schemaHasRequiredFields(schema *bigquery.TableSchema) bool {
}
return false
}
func expandForeignTypeInfo(configured interface{}) *bigquery.ForeignTypeInfo {
if len(configured.([]interface{})) == 0 {
return nil
}

raw := configured.([]interface{})[0].(map[string]interface{})
fti := &bigquery.ForeignTypeInfo{}

if v, ok := raw["type_system"]; ok {
fti.TypeSystem = v.(string)
}

return fti
}

func flattenForeignTypeInfo(fti *bigquery.ForeignTypeInfo) []map[string]interface{} {
if fti == nil {
return nil
}

result := map[string]interface{}{"type_system": fti.TypeSystem}
return []map[string]interface{}{result}
}
func expandTimePartitioning(configured interface{}) *bigquery.TimePartitioning {
raw := configured.([]interface{})[0].(map[string]interface{})
tp := &bigquery.TimePartitioning{Type: raw["type"].(string)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ func TestBigQueryTableSchemaDiffSuppress(t *testing.T) {
]`,
ExpectDiffSuppress: true,
},
"foreignTypeDefinition from generated schema -> original schema": {
Old: "[{\"name\": \"someValue\", \"type\": \"RECORD\", \"foreignTypeDefinition\" : \"STRUCT<id:STRING, name:STRING>\", \"fields\": [{\"name\": \"id\", \"type\": \"STRING\"}, {\"name\": \"name\", \"type\": \"STRING\"}]}]",
New: "[{\"name\": \"someValue\", \"type\": \"FOREIGN\", \"foreignTypeDefinition\" : \"STRUCT<id:STRING, name:STRING>\"}]",
ExpectDiffSuppress: true,
},
}

for tn, tc := range cases {
Expand All @@ -378,10 +383,10 @@ func TestBigQueryTableSchemaDiffSuppress(t *testing.T) {

var a, b interface{}
if err := json.Unmarshal([]byte(tc.Old), &a); err != nil {
t.Fatalf(fmt.Sprintf("unable to unmarshal old json - %v", err))
t.Fatalf("%v", fmt.Sprintf("unable to unmarshal old json - %v", err))
}
if err := json.Unmarshal([]byte(tc.New), &b); err != nil {
t.Fatalf(fmt.Sprintf("unable to unmarshal new json - %v", err))
t.Fatalf("%v", fmt.Sprintf("unable to unmarshal new json - %v", err))
}
if bigQueryTableSchemaDiffSuppress("schema", tc.Old, tc.New, nil) != tc.ExpectDiffSuppress {
t.Fatalf("bad: %s, %q => %q expect DiffSuppress to return %t", tn, tc.Old, tc.New, tc.ExpectDiffSuppress)
Expand Down Expand Up @@ -579,6 +584,12 @@ var testUnitBigQueryDataTableIsChangeableTestCases = []testUnitBigQueryDataTable
]`,
changeable: true,
},
{
name: "foreignTypeDefinition",
jsonOld: "[{\"name\": \"someValue\", \"type\" : \"FOREIGN\", \"foreignTypeDefinition\" : \"INTEGER\" }]",
jsonNew: "[{\"name\": \"someValue\", \"type\" : \"FOREIGN\", \"foreignTypeDefinition\" : \"STRING\" }]",
changeable: true,
},
}

func TestUnitBigQueryDataTable_schemaIsChangeable(t *testing.T) {
Expand Down
60 changes: 60 additions & 0 deletions google-beta/services/bigquery/resource_bigquery_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1851,6 +1851,33 @@ func TestAccBigQueryTable_externalCatalogTableOptions(t *testing.T) {
},
})
}

func TestAccBigQueryTable_foreignTypeInfo(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"project_id": envvar.GetTestProjectFromEnv(),
"dataset_id": fmt.Sprintf("tf_test_dataset_%s", acctest.RandString(t, 10)),
"table_id": fmt.Sprintf("tf_test_table_%s", acctest.RandString(t, 10)),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t),
CheckDestroy: testAccCheckBigQueryTableDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccBigQueryTable_foreignTypeInfo_basic(context),
},
{
ResourceName: "google_bigquery_table.test",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
},
})
}
func testAccCheckBigQueryExtData(t *testing.T, expectedQuoteChar string) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
Expand Down Expand Up @@ -4666,6 +4693,39 @@ EOF
`, context)
}

func testAccBigQueryTable_foreignTypeInfo_basic(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_bigquery_dataset" "test" {
provider = google-beta

dataset_id = "%{dataset_id}"
location = "EU"
}

resource "google_bigquery_table" "test" {
provider = google-beta

deletion_protection = false
dataset_id = "${google_bigquery_dataset.test.dataset_id}"
table_id = "%{table_id}"

schema = <<EOF
[
{
"name": "struct_",
"type": "FOREIGN",
"foreignTypeDefinition": "STRUCT<id:STRING, name:STRING>"
}
]
EOF

schema_foreign_type_info {
type_system = "HIVE"
}
}
`, context)
}

var TEST_CSV = `lifelock,LifeLock,,web,Tempe,AZ,1-May-07,6850000,USD,b
lifelock,LifeLock,,web,Tempe,AZ,1-Oct-06,6000000,USD,a
lifelock,LifeLock,,web,Tempe,AZ,1-Jan-08,25000000,USD,c
Expand Down
11 changes: 11 additions & 0 deletions website/docs/r/bigquery_table.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ The following arguments are supported:
with `external_data_configuration.schema`. Otherwise, schemas must be
specified with this top-level field.

* `schema_foreign_type_info` - (Optional, [Beta]
(https://terraform.io/docs/providers/google/guides/provider_versions.html))
Specifies metadata of the foreign data type definition in field schema.
Structure is [documented below](#nested_schema_foreign_type_info).

* `time_partitioning` - (Optional) If specified, configures time-based
partitioning for this table. Structure is [documented below](#nested_time_partitioning).

Expand Down Expand Up @@ -374,6 +379,12 @@ The following arguments are supported:

* `enable_list_inference` - (Optional) Indicates whether to use schema inference specifically for Parquet LIST logical type.

<a name="nested_schema_foreign_type_info"></a>The `schema_foreign_type_info` block supports:

* `type_system` - (Required, [Beta]
(https://terraform.io/docs/providers/google/guides/provider_versions.html))
Specifies the system which defines the foreign data type.

<a name="nested_time_partitioning"></a>The `time_partitioning` block supports:

* `expiration_ms` - (Optional) Number of milliseconds for which to keep the
Expand Down
Loading