@@ -203,8 +203,8 @@ def __init__(
203203 self ._properties ["rangeElementType" ] = {"type" : range_element_type }
204204 if isinstance (range_element_type , FieldElementType ):
205205 self ._properties ["rangeElementType" ] = range_element_type .to_api_repr ()
206-
207- self ._fields = tuple ( fields )
206+ if fields : # Don't set the property if it's not set.
207+ self ._properties [ "fields" ] = [ field . to_api_repr () for field in fields ]
208208
209209 @staticmethod
210210 def __get_int (api_repr , name ):
@@ -225,43 +225,19 @@ def from_api_repr(cls, api_repr: dict) -> "SchemaField":
225225 Returns:
226226 google.cloud.bigquery.schema.SchemaField: The ``SchemaField`` object.
227227 """
228- field_type = api_repr ["type" ].upper ()
229-
230- # Handle optional properties with default values
231- mode = api_repr .get ("mode" , "NULLABLE" )
232- description = api_repr .get ("description" , _DEFAULT_VALUE )
233- fields = api_repr .get ("fields" , ())
234- policy_tags = api_repr .get ("policyTags" , _DEFAULT_VALUE )
228+ placeholder = cls ("this_will_be_replaced" , "PLACEHOLDER" )
235229
236- default_value_expression = api_repr .get ("defaultValueExpression" , None )
230+ # Note: we don't make a copy of api_repr because this can cause
231+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
232+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
233+ placeholder ._properties = api_repr
237234
238- if policy_tags is not None and policy_tags is not _DEFAULT_VALUE :
239- policy_tags = PolicyTagList .from_api_repr (policy_tags )
240-
241- if api_repr .get ("rangeElementType" ):
242- range_element_type = cast (dict , api_repr .get ("rangeElementType" ))
243- element_type = range_element_type .get ("type" )
244- else :
245- element_type = None
246-
247- return cls (
248- field_type = field_type ,
249- fields = [cls .from_api_repr (f ) for f in fields ],
250- mode = mode .upper (),
251- default_value_expression = default_value_expression ,
252- description = description ,
253- name = api_repr ["name" ],
254- policy_tags = policy_tags ,
255- precision = cls .__get_int (api_repr , "precision" ),
256- scale = cls .__get_int (api_repr , "scale" ),
257- max_length = cls .__get_int (api_repr , "maxLength" ),
258- range_element_type = element_type ,
259- )
235+ return placeholder
260236
261237 @property
262238 def name (self ):
263239 """str: The name of the field."""
264- return self ._properties [ "name" ]
240+ return self ._properties . get ( "name" , "" )
265241
266242 @property
267243 def field_type (self ):
@@ -270,7 +246,10 @@ def field_type(self):
270246 See:
271247 https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.type
272248 """
273- return self ._properties ["type" ]
249+ type_ = self ._properties .get ("type" )
250+ if type_ is None : # Shouldn't happen, but some unit tests do this.
251+ return None
252+ return type_ .upper ()
274253
275254 @property
276255 def mode (self ):
@@ -279,7 +258,7 @@ def mode(self):
279258 See:
280259 https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.mode
281260 """
282- return self ._properties .get ("mode" )
261+ return self ._properties .get ("mode" , "NULLABLE" ). upper ( )
283262
284263 @property
285264 def is_nullable (self ):
@@ -329,7 +308,7 @@ def fields(self):
329308
330309 Must be empty unset if ``field_type`` is not 'RECORD'.
331310 """
332- return self ._fields
311+ return tuple ( _to_schema_fields ( self ._properties . get ( "fields" , [])))
333312
334313 @property
335314 def policy_tags (self ):
@@ -345,14 +324,10 @@ def to_api_repr(self) -> dict:
345324 Returns:
346325 Dict: A dictionary representing the SchemaField in a serialized form.
347326 """
348- answer = self ._properties .copy ()
349-
350- # If this is a RECORD type, then sub-fields are also included,
351- # add this to the serialized representation.
352- if self .field_type .upper () in _STRUCT_TYPES :
353- answer ["fields" ] = [f .to_api_repr () for f in self .fields ]
354-
355- # Done; return the serialized dictionary.
327+ # Note: we don't make a copy of _properties because this can cause
328+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
329+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
330+ answer = self ._properties
356331 return answer
357332
358333 def _key (self ):
@@ -389,7 +364,7 @@ def _key(self):
389364 self .mode .upper (), # pytype: disable=attribute-error
390365 self .default_value_expression ,
391366 self .description ,
392- self ._fields ,
367+ self .fields ,
393368 policy_tags ,
394369 )
395370
0 commit comments