@@ -1361,6 +1361,13 @@ func ResourceBigQueryTable() *schema.Resource {
1361
1361
Description : `Whether Terraform will be prevented from destroying the instance. When the field is set to true or unset in Terraform state, a terraform apply or terraform destroy that would delete the table will fail. When the field is set to false, deleting the table is allowed.` ,
1362
1362
},
1363
1363
1364
+ "ignore_auto_generated_schema" : {
1365
+ Type : schema .TypeBool ,
1366
+ Optional : true ,
1367
+ Default : false ,
1368
+ Description : `Whether Terraform will prevent implicitly added columns in schema from showing diff.` ,
1369
+ },
1370
+
1364
1371
// TableConstraints: [Optional] Defines the primary key and foreign keys.
1365
1372
"table_constraints" : {
1366
1373
Type : schema .TypeList ,
@@ -1610,6 +1617,49 @@ func ResourceBigQueryTable() *schema.Resource {
1610
1617
}
1611
1618
}
1612
1619
1620
+ // filterLiveSchemaByConfig compares a live schema from the BQ API with a schema from
1621
+ // the Terraform config. It returns a new schema containing only the fields
1622
+ // that are defined in the config, effectively removing any columns that were
1623
+ // auto-generated by the service (e.g., hive partitioning keys).
1624
+ //
1625
+ // Parameters:
1626
+ // - liveSchema: The schema returned from a BigQuery API Read/Get call. This may contain extra columns.
1627
+ // - configSchema: The schema built from the user's Terraform configuration (`d.Get("schema")`). This is the source of truth.
1628
+ //
1629
+ // Returns:
1630
+ //
1631
+ // A new *bigquery.TableSchema containing a filtered list of fields.
1632
+ func filterLiveSchemaByConfig (liveSchema * bigquery.TableSchema , configSchema * bigquery.TableSchema ) * bigquery.TableSchema {
1633
+ if liveSchema == nil || configSchema == nil {
1634
+ // If either schema is nil, there's nothing to compare, so return an empty schema.
1635
+ return & bigquery.TableSchema {Fields : []* bigquery.TableFieldSchema {}}
1636
+ }
1637
+
1638
+ // 1. Create a lookup map of all column names defined in the configuration.
1639
+ // This provides fast O(1) average time complexity for lookups.
1640
+ configFieldsMap := make (map [string ]bool )
1641
+ for _ , field := range configSchema .Fields {
1642
+ configFieldsMap [field .Name ] = true
1643
+ }
1644
+
1645
+ // 2. Iterate through the fields in the live schema and keep only the ones
1646
+ // that exist in our configuration map.
1647
+ var filteredFields []* bigquery.TableFieldSchema
1648
+ for _ , liveField := range liveSchema .Fields {
1649
+ // If the live field's name is present in the map of configured fields...
1650
+ if _ , ok := configFieldsMap [liveField .Name ]; ok {
1651
+ // ...then it's a field we care about. Add it to our filtered list.
1652
+ filteredFields = append (filteredFields , liveField )
1653
+ } else {
1654
+ log .Printf ("[DEBUG] auto-generated column `%s` dropped during Table read." , liveField .Name )
1655
+ }
1656
+ }
1657
+
1658
+ return & bigquery.TableSchema {
1659
+ Fields : filteredFields ,
1660
+ }
1661
+ }
1662
+
1613
1663
func resourceTable (d * schema.ResourceData , meta interface {}) (* bigquery.Table , error ) {
1614
1664
config := meta .(* transport_tpg.Config )
1615
1665
@@ -1999,7 +2049,17 @@ func resourceBigQueryTableRead(d *schema.ResourceData, meta interface{}) error {
1999
2049
}
2000
2050
2001
2051
if res .Schema != nil {
2002
- schema , err := flattenSchema (res .Schema )
2052
+ table , err := resourceTable (d , meta )
2053
+ if err != nil {
2054
+ return err
2055
+ }
2056
+
2057
+ schemaFiltered := res .Schema
2058
+ ignore , ok := d .Get ("ignore_auto_generated_schema" ).(bool )
2059
+ if ok && ignore {
2060
+ schemaFiltered = filterLiveSchemaByConfig (res .Schema , table .Schema )
2061
+ }
2062
+ schema , err := flattenSchema (schemaFiltered )
2003
2063
if err != nil {
2004
2064
return err
2005
2065
}
@@ -2086,7 +2146,7 @@ type TableReference struct {
2086
2146
2087
2147
func resourceBigQueryTableUpdate (d * schema.ResourceData , meta interface {}) error {
2088
2148
// If only client-side fields were modified, short-circuit the Update function to avoid sending an update API request.
2089
- clientSideFields := map [string ]bool {"deletion_protection" : true , "ignore_schema_changes" : true , "table_metadata_view" : true }
2149
+ clientSideFields := map [string ]bool {"deletion_protection" : true , "ignore_schema_changes" : true , "ignore_auto_generated_schema" : true , " table_metadata_view" : true }
2090
2150
clientSideOnly := true
2091
2151
for field := range ResourceBigQueryTable ().Schema {
2092
2152
if d .HasChange (field ) && ! clientSideFields [field ] {
@@ -2134,8 +2194,11 @@ func resourceBigQueryTableUpdate(d *schema.ResourceData, meta interface{}) error
2134
2194
tableID : tableID ,
2135
2195
}
2136
2196
2137
- if err = resourceBigQueryTableColumnDrop (config , userAgent , table , tableReference , tableMetadataView ); err != nil {
2138
- return err
2197
+ // If we are supposed to ignore server generated schema columns, we don't need to drop them
2198
+ if ! d .Get ("ignore_auto_generated_schema" ).(bool ) {
2199
+ if err = resourceBigQueryTableColumnDrop (config , userAgent , table , tableReference , tableMetadataView ); err != nil {
2200
+ return err
2201
+ }
2139
2202
}
2140
2203
2141
2204
if _ , err = config .NewBigQueryClient (userAgent ).Tables .Update (project , datasetID , tableID , table ).Do (); err != nil {
0 commit comments