diff --git a/bigquery/google/cloud/bigquery/_helpers.py b/bigquery/google/cloud/bigquery/_helpers.py index 3108afcaf258..c0a29b427b3b 100644 --- a/bigquery/google/cloud/bigquery/_helpers.py +++ b/bigquery/google/cloud/bigquery/_helpers.py @@ -176,6 +176,7 @@ def _record_from_json(value, field): 'BOOLEAN': _bool_from_json, 'BOOL': _bool_from_json, 'STRING': _string_from_json, + 'GEOGRAPHY': _string_from_json, 'BYTES': _bytes_from_json, 'TIMESTAMP': _timestamp_from_json, 'DATETIME': _datetime_from_json, diff --git a/bigquery/google/cloud/bigquery/schema.py b/bigquery/google/cloud/bigquery/schema.py index cc1b4a5ff024..759d7c3cbe65 100644 --- a/bigquery/google/cloud/bigquery/schema.py +++ b/bigquery/google/cloud/bigquery/schema.py @@ -18,23 +18,19 @@ class SchemaField(object): """Describe a single field within a table schema. - :type name: str - :param name: the name of the field. + Args: + name (str): the name of the field. - :type field_type: str - :param field_type: the type of the field (one of 'STRING', 'INTEGER', - 'FLOAT', 'NUMERIC', 'BOOLEAN', 'TIMESTAMP' or - 'RECORD'). + field_type (str): the type of the field. See + https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#schema.fields.type - :type mode: str - :param mode: the mode of the field (one of 'NULLABLE', 'REQUIRED', - or 'REPEATED'). + mode (str): the mode of the field. See + https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#schema.fields.mode - :type description: str - :param description: optional description for the field. + description (Optional[str]):description for the field. - :type fields: tuple of :class:`~google.cloud.bigquery.schema.SchemaField` - :param fields: subfields (requires ``field_type`` of 'RECORD'). + fields (Tuple[:class:`~google.cloud.bigquery.schema.SchemaField`]): + subfields (requires ``field_type`` of 'RECORD'). """ def __init__(self, name, field_type, mode='NULLABLE', description=None, fields=()): @@ -78,8 +74,8 @@ def name(self): def field_type(self): """str: The type of the field. - Will be one of 'STRING', 'INTEGER', 'FLOAT', 'NUMERIC', - 'BOOLEAN', 'TIMESTAMP' or 'RECORD'. + See: + https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#schema.fields.type """ return self._field_type @@ -87,26 +83,26 @@ def field_type(self): def mode(self): """str: The mode of the field. - Will be one of 'NULLABLE', 'REQUIRED', or 'REPEATED'. + See: + https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#schema.fields.mode """ return self._mode @property def is_nullable(self): - """Check whether 'mode' is 'nullable'.""" + """bool: whether 'mode' is 'nullable'.""" return self._mode == 'NULLABLE' @property def description(self): - """Optional[str]: Description for the field.""" + """Optional[str]: description for the field.""" return self._description @property def fields(self): """tuple: Subfields contained in this field. - If ``field_type`` is not 'RECORD', this property must be - empty / unset. + Must be empty unset if ``field_type`` is not 'RECORD'. """ return self._fields @@ -168,14 +164,12 @@ def __repr__(self): def _parse_schema_resource(info): """Parse a resource fragment into a schema field. - :type info: mapping - :param info: should contain a "fields" key to be parsed + Args: + info: (Mapping[str->dict]): should contain a "fields" key to be parsed - :rtype: - list of :class:`google.cloud.bigquery.schema.SchemaField`, or - ``NoneType`` - :returns: a list of parsed fields, or ``None`` if no "fields" key is - present in ``info``. + Returns: + (Union[Sequence[:class:`google.cloud.bigquery.schema.SchemaField`],None]) + a list of parsed fields, or ``None`` if no "fields" key found. """ if 'fields' not in info: return () @@ -195,11 +189,11 @@ def _parse_schema_resource(info): def _build_schema_resource(fields): """Generate a resource fragment for a schema. - :type fields: - sequence of :class:`~google.cloud.bigquery.schema.SchemaField` - :param fields: schema to be dumped + Args: + fields [Sequence[:class:`~google.cloud.bigquery.schema.SchemaField`]): + schema to be dumped - :rtype: mapping - :returns: a mapping describing the schema of the supplied fields. + Returns: (Sequence[dict]) + mappings describing the schema of the supplied fields. """ return [field.to_api_repr() for field in fields] diff --git a/bigquery/tests/system.py b/bigquery/tests/system.py index e70dcb26981d..1cfc46c4736e 100644 --- a/bigquery/tests/system.py +++ b/bigquery/tests/system.py @@ -1019,6 +1019,10 @@ def _generate_standard_sql_types_examples(self): 'sql': 'SELECT ARRAY(SELECT STRUCT([1, 2]))', 'expected': [{u'_field_1': [1, 2]}], }, + { + 'sql': 'SELECT ST_GeogPoint(1, 2)', + 'expected': 'POINT(1 2)', + }, ] def test_query_w_standard_sql_types(self): diff --git a/bigquery/tests/unit/test__helpers.py b/bigquery/tests/unit/test__helpers.py index 906119e453e2..d0a93ebd1340 100644 --- a/bigquery/tests/unit/test__helpers.py +++ b/bigquery/tests/unit/test__helpers.py @@ -376,6 +376,13 @@ def test_w_scalar_subfield(self): coerced = self._call_fut(value, field) self.assertEqual(coerced, {'age': 42}) + def test_w_scalar_subfield_geography(self): + subfield = _Field('REQUIRED', 'geo', 'GEOGRAPHY') + field = _Field('REQUIRED', fields=[subfield]) + value = {'f': [{'v': 'POINT(1, 2)'}]} + coerced = self._call_fut(value, field) + self.assertEqual(coerced, {'geo': 'POINT(1, 2)'}) + def test_w_repeated_subfield(self): subfield = _Field('REPEATED', 'color', 'STRING') field = _Field('REQUIRED', fields=[subfield]) @@ -444,6 +451,12 @@ def test_w_single_scalar_column(self): row = {u'f': [{u'v': u'1'}]} self.assertEqual(self._call_fut(row, schema=[col]), (1,)) + def test_w_single_scalar_geography_column(self): + # SELECT 1 AS col + col = _Field('REQUIRED', 'geo', 'GEOGRAPHY') + row = {u'f': [{u'v': u'POINT(1, 2)'}]} + self.assertEqual(self._call_fut(row, schema=[col]), ('POINT(1, 2)',)) + def test_w_single_struct_column(self): # SELECT (1, 2) AS col sub_1 = _Field('REQUIRED', 'sub_1', 'INTEGER')