16
16
17
17
import collections
18
18
import enum
19
- from typing import Any , Dict , Iterable , Optional , Union , cast
19
+ from typing import Any , cast , Dict , Iterable , Optional , Union
20
20
21
+ from google .cloud .bigquery import _helpers
21
22
from google .cloud .bigquery import standard_sql
22
23
from google .cloud .bigquery .enums import StandardSqlTypeNames
23
24
@@ -203,15 +204,8 @@ def __init__(
203
204
self ._properties ["rangeElementType" ] = {"type" : range_element_type }
204
205
if isinstance (range_element_type , FieldElementType ):
205
206
self ._properties ["rangeElementType" ] = range_element_type .to_api_repr ()
206
-
207
- self ._fields = tuple (fields )
208
-
209
- @staticmethod
210
- def __get_int (api_repr , name ):
211
- v = api_repr .get (name , _DEFAULT_VALUE )
212
- if v is not _DEFAULT_VALUE :
213
- v = int (v )
214
- return v
207
+ if fields : # Don't set the property if it's not set.
208
+ self ._properties ["fields" ] = [field .to_api_repr () for field in fields ]
215
209
216
210
@classmethod
217
211
def from_api_repr (cls , api_repr : dict ) -> "SchemaField" :
@@ -225,43 +219,19 @@ def from_api_repr(cls, api_repr: dict) -> "SchemaField":
225
219
Returns:
226
220
google.cloud.bigquery.schema.SchemaField: The ``SchemaField`` object.
227
221
"""
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 )
222
+ placeholder = cls ("this_will_be_replaced" , "PLACEHOLDER" )
235
223
236
- default_value_expression = api_repr .get ("defaultValueExpression" , None )
224
+ # Note: we don't make a copy of api_repr because this can cause
225
+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
226
+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
227
+ placeholder ._properties = api_repr
237
228
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
- )
229
+ return placeholder
260
230
261
231
@property
262
232
def name (self ):
263
233
"""str: The name of the field."""
264
- return self ._properties [ "name" ]
234
+ return self ._properties . get ( "name" , "" )
265
235
266
236
@property
267
237
def field_type (self ):
@@ -270,7 +240,10 @@ def field_type(self):
270
240
See:
271
241
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.type
272
242
"""
273
- return self ._properties ["type" ]
243
+ type_ = self ._properties .get ("type" )
244
+ if type_ is None : # Shouldn't happen, but some unit tests do this.
245
+ return None
246
+ return cast (str , type_ ).upper ()
274
247
275
248
@property
276
249
def mode (self ):
@@ -279,7 +252,7 @@ def mode(self):
279
252
See:
280
253
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.mode
281
254
"""
282
- return self ._properties .get ("mode" )
255
+ return cast ( str , self ._properties .get ("mode" , "NULLABLE" )). upper ( )
283
256
284
257
@property
285
258
def is_nullable (self ):
@@ -299,17 +272,17 @@ def description(self):
299
272
@property
300
273
def precision (self ):
301
274
"""Optional[int]: Precision (number of digits) for the NUMERIC field."""
302
- return self ._properties .get ("precision" )
275
+ return _helpers . _int_or_none ( self ._properties .get ("precision" ) )
303
276
304
277
@property
305
278
def scale (self ):
306
279
"""Optional[int]: Scale (digits after decimal) for the NUMERIC field."""
307
- return self ._properties .get ("scale" )
280
+ return _helpers . _int_or_none ( self ._properties .get ("scale" ) )
308
281
309
282
@property
310
283
def max_length (self ):
311
284
"""Optional[int]: Maximum length for the STRING or BYTES field."""
312
- return self ._properties .get ("maxLength" )
285
+ return _helpers . _int_or_none ( self ._properties .get ("maxLength" ) )
313
286
314
287
@property
315
288
def range_element_type (self ):
@@ -329,7 +302,7 @@ def fields(self):
329
302
330
303
Must be empty unset if ``field_type`` is not 'RECORD'.
331
304
"""
332
- return self ._fields
305
+ return tuple ( _to_schema_fields ( self ._properties . get ( "fields" , [])))
333
306
334
307
@property
335
308
def policy_tags (self ):
@@ -345,15 +318,10 @@ def to_api_repr(self) -> dict:
345
318
Returns:
346
319
Dict: A dictionary representing the SchemaField in a serialized form.
347
320
"""
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.
356
- return answer
321
+ # Note: we don't make a copy of _properties because this can cause
322
+ # unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
323
+ # fields. See https://github.com/googleapis/python-bigquery/issues/6
324
+ return self ._properties
357
325
358
326
def _key (self ):
359
327
"""A tuple key that uniquely describes this field.
@@ -389,7 +357,7 @@ def _key(self):
389
357
self .mode .upper (), # pytype: disable=attribute-error
390
358
self .default_value_expression ,
391
359
self .description ,
392
- self ._fields ,
360
+ self .fields ,
393
361
policy_tags ,
394
362
)
395
363
0 commit comments