2020
2121import copy
2222import enum
23- from typing import Any , Dict , Iterable , Optional , Union , cast , List , Mapping
23+ from typing import Any , cast , Dict , Iterable , Optional , Union
2424
25+ from google .cloud .bigquery import _helpers
2526from google .cloud .bigquery import standard_sql
2627from google .cloud .bigquery ._helpers import (
2728 _isinstance_or_raise ,
@@ -218,7 +219,10 @@ def __init__(
218219 rounding_mode : Union [RoundingMode , str , None ] = None ,
219220 foreign_type_definition : Optional [str ] = None ,
220221 ):
221- self ._properties : Dict [str , Any ] = {}
222+ self ._properties : Dict [str , Any ] = {
223+ "name" : name ,
224+ "type" : field_type ,
225+ }
222226
223227 self ._properties ["name" ] = name
224228 if mode is not None :
@@ -257,16 +261,8 @@ def __init__(
257261 raise ValueError (
258262 "If the 'field_type' is 'FOREIGN', then 'foreign_type_definition' is required."
259263 )
260- self ._properties ["type" ] = field_type
261-
262- self ._fields = tuple (fields )
263-
264- @staticmethod
265- def __get_int (api_repr , name ):
266- v = api_repr .get (name , _DEFAULT_VALUE )
267- if v is not _DEFAULT_VALUE :
268- v = int (v )
269- return v
264+ if fields : # Don't set the property if it's not set.
265+ self ._properties ["fields" ] = [field .to_api_repr () for field in fields ]
270266
271267 @classmethod
272268 def from_api_repr (cls , api_repr : Mapping [str , Any ]) -> "SchemaField" :
@@ -280,48 +276,19 @@ def from_api_repr(cls, api_repr: Mapping[str, Any]) -> "SchemaField":
280276 Returns:
281277 google.cloud.bigquery.schema.SchemaField: The ``SchemaField`` object.
282278 """
283- field_type = api_repr [ "type" ]. upper ( )
279+ placeholder = cls ( "this_will_be_replaced" , "PLACEHOLDER" )
284280
285- # Handle optional properties with default values
286- mode = api_repr .get ("mode" , "NULLABLE" )
287- description = api_repr .get ("description" , _DEFAULT_VALUE )
288- fields = api_repr .get ("fields" , ())
289- policy_tags = api_repr .get ("policyTags" , _DEFAULT_VALUE )
281+ # Note: we don't make a copy of api_repr because this can cause
282+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
283+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
284+ placeholder ._properties = api_repr
290285
291- default_value_expression = api_repr .get ("defaultValueExpression" , None )
292-
293- if policy_tags is not None and policy_tags is not _DEFAULT_VALUE :
294- policy_tags = PolicyTagList .from_api_repr (policy_tags )
295-
296- if api_repr .get ("rangeElementType" ):
297- range_element_type = cast (dict , api_repr .get ("rangeElementType" ))
298- element_type = range_element_type .get ("type" )
299- else :
300- element_type = None
301-
302- rounding_mode = api_repr .get ("roundingMode" )
303- foreign_type_definition = api_repr .get ("foreignTypeDefinition" )
304-
305- return cls (
306- field_type = field_type ,
307- fields = [cls .from_api_repr (f ) for f in fields ],
308- mode = mode .upper (),
309- default_value_expression = default_value_expression ,
310- description = description ,
311- name = api_repr ["name" ],
312- policy_tags = policy_tags ,
313- precision = cls .__get_int (api_repr , "precision" ),
314- scale = cls .__get_int (api_repr , "scale" ),
315- max_length = cls .__get_int (api_repr , "maxLength" ),
316- range_element_type = element_type ,
317- rounding_mode = rounding_mode ,
318- foreign_type_definition = foreign_type_definition ,
319- )
286+ return placeholder
320287
321288 @property
322289 def name (self ):
323290 """str: The name of the field."""
324- return self ._properties [ "name" ]
291+ return self ._properties . get ( "name" , "" )
325292
326293 @property
327294 def field_type (self ):
@@ -330,7 +297,10 @@ def field_type(self):
330297 See:
331298 https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.type
332299 """
333- return self ._properties ["type" ]
300+ type_ = self ._properties .get ("type" )
301+ if type_ is None : # Shouldn't happen, but some unit tests do this.
302+ return None
303+ return cast (str , type_ ).upper ()
334304
335305 @property
336306 def mode (self ):
@@ -339,7 +309,7 @@ def mode(self):
339309 See:
340310 https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.mode
341311 """
342- return self ._properties .get ("mode" )
312+ return cast ( str , self ._properties .get ("mode" , "NULLABLE" )). upper ( )
343313
344314 @property
345315 def is_nullable (self ):
@@ -359,17 +329,17 @@ def description(self):
359329 @property
360330 def precision (self ):
361331 """Optional[int]: Precision (number of digits) for the NUMERIC field."""
362- return self ._properties .get ("precision" )
332+ return _helpers . _int_or_none ( self ._properties .get ("precision" ) )
363333
364334 @property
365335 def scale (self ):
366336 """Optional[int]: Scale (digits after decimal) for the NUMERIC field."""
367- return self ._properties .get ("scale" )
337+ return _helpers . _int_or_none ( self ._properties .get ("scale" ) )
368338
369339 @property
370340 def max_length (self ):
371341 """Optional[int]: Maximum length for the STRING or BYTES field."""
372- return self ._properties .get ("maxLength" )
342+ return _helpers . _int_or_none ( self ._properties .get ("maxLength" ) )
373343
374344 @property
375345 def range_element_type (self ):
@@ -405,7 +375,7 @@ def fields(self):
405375
406376 Must be empty unset if ``field_type`` is not 'RECORD'.
407377 """
408- return self ._fields
378+ return tuple ( _to_schema_fields ( self ._properties . get ( "fields" , [])))
409379
410380 @property
411381 def policy_tags (self ):
@@ -421,15 +391,10 @@ def to_api_repr(self) -> dict:
421391 Returns:
422392 Dict: A dictionary representing the SchemaField in a serialized form.
423393 """
424- answer = self ._properties .copy ()
425-
426- # If this is a RECORD type, then sub-fields are also included,
427- # add this to the serialized representation.
428- if self .field_type .upper () in _STRUCT_TYPES :
429- answer ["fields" ] = [f .to_api_repr () for f in self .fields ]
430-
431- # Done; return the serialized dictionary.
432- return answer
394+ # Note: we don't make a copy of _properties because this can cause
395+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
396+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
397+ return self ._properties
433398
434399 def _key (self ):
435400 """A tuple key that uniquely describes this field.
@@ -465,7 +430,7 @@ def _key(self):
465430 self .mode .upper (), # pytype: disable=attribute-error
466431 self .default_value_expression ,
467432 self .description ,
468- self ._fields ,
433+ self .fields ,
469434 policy_tags ,
470435 )
471436
0 commit comments