20
20
21
21
import copy
22
22
import enum
23
- from typing import Any , Dict , Iterable , Optional , Union , cast , List , Mapping
23
+ from typing import Any , cast , Dict , Iterable , Optional , Union
24
24
25
+ from google .cloud .bigquery import _helpers
25
26
from google .cloud .bigquery import standard_sql
26
27
from google .cloud .bigquery ._helpers import (
27
28
_isinstance_or_raise ,
@@ -218,7 +219,10 @@ def __init__(
218
219
rounding_mode : Union [RoundingMode , str , None ] = None ,
219
220
foreign_type_definition : Optional [str ] = None ,
220
221
):
221
- self ._properties : Dict [str , Any ] = {}
222
+ self ._properties : Dict [str , Any ] = {
223
+ "name" : name ,
224
+ "type" : field_type ,
225
+ }
222
226
223
227
self ._properties ["name" ] = name
224
228
if mode is not None :
@@ -259,14 +263,9 @@ def __init__(
259
263
)
260
264
self ._properties ["type" ] = field_type
261
265
262
- self ._fields = tuple (fields )
266
+ if fields : # Don't set the property if it's not set.
267
+ self ._properties ["fields" ] = [field .to_api_repr () for field in fields ]
263
268
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
270
269
271
270
@classmethod
272
271
def from_api_repr (cls , api_repr : Mapping [str , Any ]) -> "SchemaField" :
@@ -280,48 +279,19 @@ def from_api_repr(cls, api_repr: Mapping[str, Any]) -> "SchemaField":
280
279
Returns:
281
280
google.cloud.bigquery.schema.SchemaField: The ``SchemaField`` object.
282
281
"""
283
- field_type = api_repr ["type" ].upper ()
284
-
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 )
282
+ placeholder = cls ("this_will_be_replaced" , "PLACEHOLDER" )
290
283
291
- default_value_expression = api_repr .get ("defaultValueExpression" , None )
284
+ # Note: we don't make a copy of api_repr because this can cause
285
+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
286
+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
287
+ placeholder ._properties = api_repr
292
288
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
- )
289
+ return placeholder
320
290
321
291
@property
322
292
def name (self ):
323
293
"""str: The name of the field."""
324
- return self ._properties [ "name" ]
294
+ return self ._properties . get ( "name" , "" )
325
295
326
296
@property
327
297
def field_type (self ):
@@ -330,7 +300,10 @@ def field_type(self):
330
300
See:
331
301
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.type
332
302
"""
333
- return self ._properties ["type" ]
303
+ type_ = self ._properties .get ("type" )
304
+ if type_ is None : # Shouldn't happen, but some unit tests do this.
305
+ return None
306
+ return cast (str , type_ ).upper ()
334
307
335
308
@property
336
309
def mode (self ):
@@ -339,7 +312,7 @@ def mode(self):
339
312
See:
340
313
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.mode
341
314
"""
342
- return self ._properties .get ("mode" )
315
+ return cast ( str , self ._properties .get ("mode" , "NULLABLE" )). upper ( )
343
316
344
317
@property
345
318
def is_nullable (self ):
@@ -359,17 +332,17 @@ def description(self):
359
332
@property
360
333
def precision (self ):
361
334
"""Optional[int]: Precision (number of digits) for the NUMERIC field."""
362
- return self ._properties .get ("precision" )
335
+ return _helpers . _int_or_none ( self ._properties .get ("precision" ) )
363
336
364
337
@property
365
338
def scale (self ):
366
339
"""Optional[int]: Scale (digits after decimal) for the NUMERIC field."""
367
- return self ._properties .get ("scale" )
340
+ return _helpers . _int_or_none ( self ._properties .get ("scale" ) )
368
341
369
342
@property
370
343
def max_length (self ):
371
344
"""Optional[int]: Maximum length for the STRING or BYTES field."""
372
- return self ._properties .get ("maxLength" )
345
+ return _helpers . _int_or_none ( self ._properties .get ("maxLength" ) )
373
346
374
347
@property
375
348
def range_element_type (self ):
@@ -405,7 +378,7 @@ def fields(self):
405
378
406
379
Must be empty unset if ``field_type`` is not 'RECORD'.
407
380
"""
408
- return self ._fields
381
+ return tuple ( _to_schema_fields ( self ._properties . get ( "fields" , [])))
409
382
410
383
@property
411
384
def policy_tags (self ):
@@ -421,15 +394,10 @@ def to_api_repr(self) -> dict:
421
394
Returns:
422
395
Dict: A dictionary representing the SchemaField in a serialized form.
423
396
"""
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
397
+ # Note: we don't make a copy of _properties because this can cause
398
+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
399
+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
400
+ return self ._properties
433
401
434
402
def _key (self ):
435
403
"""A tuple key that uniquely describes this field.
@@ -465,7 +433,7 @@ def _key(self):
465
433
self .mode .upper (), # pytype: disable=attribute-error
466
434
self .default_value_expression ,
467
435
self .description ,
468
- self ._fields ,
436
+ self .fields ,
469
437
policy_tags ,
470
438
)
471
439
0 commit comments