From bfb4060a53a6942c3fa456dc9be4c6a11770eed3 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 20 Dec 2016 15:06:21 -0500 Subject: [PATCH 1/2] Conform to expected wire format for struct query parameters. Closes: #2887. --- bigquery/google/cloud/bigquery/_helpers.py | 12 ++++-- bigquery/unit_tests/test__helpers.py | 44 ++++++++++++++++------ system_tests/bigquery.py | 14 +++++++ 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/bigquery/google/cloud/bigquery/_helpers.py b/bigquery/google/cloud/bigquery/_helpers.py index b72b55950b71..8b83acc6d53f 100644 --- a/bigquery/google/cloud/bigquery/_helpers.py +++ b/bigquery/google/cloud/bigquery/_helpers.py @@ -501,6 +501,7 @@ def to_api_repr(self): values = [converter(value) for value in values] resource = { 'parameterType': { + 'type': 'ARRAY', 'arrayType': self.array_type, }, 'parameterValue': { @@ -554,10 +555,12 @@ def from_api_repr(cls, resource): instance = cls(name) types = instance.struct_types for item in resource['parameterType']['structTypes']: - types[item['name']] = item['type'] + types[item['name']] = item['type']['type'] struct_values = resource['parameterValue']['structValues'] for key, value in struct_values.items(): - converted = _CELLDATA_FROM_JSON[types[key]](value, None) + type_ = types[key] + value = value['value'] + converted = _CELLDATA_FROM_JSON[type_](value, None) instance.struct_values[key] = converted return instance @@ -568,7 +571,7 @@ def to_api_repr(self): :returns: JSON mapping """ types = [ - {'name': key, 'type': value} + {'name': key, 'type': {'type': value}} for key, value in self.struct_types.items() ] values = {} @@ -576,10 +579,11 @@ def to_api_repr(self): converter = _SCALAR_VALUE_TO_JSON.get(self.struct_types[name]) if converter is not None: value = converter(value) - values[name] = value + values[name] = {'value': value} resource = { 'parameterType': { + 'type': 'STRUCT', 'structTypes': types, }, 'parameterValue': { diff --git a/bigquery/unit_tests/test__helpers.py b/bigquery/unit_tests/test__helpers.py index affd52294fdd..465e5defc143 100644 --- a/bigquery/unit_tests/test__helpers.py +++ b/bigquery/unit_tests/test__helpers.py @@ -1082,6 +1082,7 @@ def test_from_api_repr_w_name(self): def test_from_api_repr_wo_name(self): RESOURCE = { 'parameterType': { + 'type': 'ARRAY', 'arrayType': 'INT64', }, 'parameterValue': { @@ -1098,6 +1099,7 @@ def test_to_api_repr_w_name(self): EXPECTED = { 'name': 'foo', 'parameterType': { + 'type': 'ARRAY', 'arrayType': 'INT64', }, 'parameterValue': { @@ -1110,6 +1112,7 @@ def test_to_api_repr_w_name(self): def test_to_api_repr_wo_name(self): EXPECTED = { 'parameterType': { + 'type': 'ARRAY', 'arrayType': 'INT64', }, 'parameterValue': { @@ -1123,6 +1126,7 @@ def test_to_api_repr_wo_name(self): def test_to_api_repr_w_unknown_type(self): EXPECTED = { 'parameterType': { + 'type': 'ARRAY', 'arrayType': 'UNKNOWN', }, 'parameterValue': { @@ -1170,13 +1174,17 @@ def test_from_api_repr_w_name(self): RESOURCE = { 'name': 'foo', 'parameterType': { + 'type': 'STRUTCT', 'structTypes': [ - {'name': 'bar', 'type': 'INT64'}, - {'name': 'baz', 'type': 'STRING'}, + {'name': 'bar', 'type': {'type': 'INT64'}}, + {'name': 'baz', 'type': {'type': 'STRING'}}, ], }, 'parameterValue': { - 'structValues': {'bar': 123, 'baz': 'abc'}, + 'structValues': { + 'bar': {'value': 123}, + 'baz': {'value': 'abc'}, + }, }, } klass = self._get_target_class() @@ -1188,13 +1196,17 @@ def test_from_api_repr_w_name(self): def test_from_api_repr_wo_name(self): RESOURCE = { 'parameterType': { + 'type': 'STRUTCT', 'structTypes': [ - {'name': 'bar', 'type': 'INT64'}, - {'name': 'baz', 'type': 'STRING'}, + {'name': 'bar', 'type': {'type': 'INT64'}}, + {'name': 'baz', 'type': {'type': 'STRING'}}, ], }, 'parameterValue': { - 'structValues': {'bar': 123, 'baz': 'abc'}, + 'structValues': { + 'bar': {'value': 123}, + 'baz': {'value': 'abc'}, + }, }, } klass = self._get_target_class() @@ -1207,13 +1219,17 @@ def test_to_api_repr_w_name(self): EXPECTED = { 'name': 'foo', 'parameterType': { + 'type': 'STRUCT', 'structTypes': [ - {'name': 'bar', 'type': 'INT64'}, - {'name': 'baz', 'type': 'STRING'}, + {'name': 'bar', 'type': {'type': 'INT64'}}, + {'name': 'baz', 'type': {'type': 'STRING'}}, ], }, 'parameterValue': { - 'structValues': {'bar': '123', 'baz': 'abc'}, + 'structValues': { + 'bar': {'value': '123'}, + 'baz': {'value': 'abc'}, + }, }, } sub_1 = self._make_subparam('bar', 'INT64', 123) @@ -1224,13 +1240,17 @@ def test_to_api_repr_w_name(self): def test_to_api_repr_wo_name(self): EXPECTED = { 'parameterType': { + 'type': 'STRUCT', 'structTypes': [ - {'name': 'bar', 'type': 'INT64'}, - {'name': 'baz', 'type': 'STRING'}, + {'name': 'bar', 'type': {'type': 'INT64'}}, + {'name': 'baz', 'type': {'type': 'STRING'}}, ], }, 'parameterValue': { - 'structValues': {'bar': '123', 'baz': 'abc'}, + 'structValues': { + 'bar': {'value': '123'}, + 'baz': {'value': 'abc'}, + }, }, } sub_1 = self._make_subparam('bar', 'INT64', 123) diff --git a/system_tests/bigquery.py b/system_tests/bigquery.py index 5b58ad2ee4da..622d63bc3788 100644 --- a/system_tests/bigquery.py +++ b/system_tests/bigquery.py @@ -483,11 +483,20 @@ def test_sync_query_w_standard_sql_types(self): import datetime from google.cloud._helpers import UTC from google.cloud.bigquery._helpers import ScalarQueryParameter + from google.cloud.bigquery._helpers import StructQueryParameter naive = datetime.datetime(2016, 12, 5, 12, 41, 9) stamp = "%s %s" % (naive.date().isoformat(), naive.time().isoformat()) zoned = naive.replace(tzinfo=UTC) zoned_param = ScalarQueryParameter( name='zoned', type_='TIMESTAMP', value=zoned) + question = 'What is the answer to life, the universe, and everything?' + question_param = ScalarQueryParameter( + name='question', type_='STRING', value=question) + answer = 42 + answer_param = ScalarQueryParameter( + name='answer', type_='INT64', value=answer) + struct_param = StructQueryParameter( + 'hitchhiker', question_param, answer_param) EXAMPLES = [ { 'sql': 'SELECT 1', @@ -561,6 +570,11 @@ def test_sync_query_w_standard_sql_types(self): 'expected': zoned, 'query_parameters': [zoned_param], }, + { + 'sql': 'SELECT (@hitchhiker.question, @hitchhiker.answer)', + 'expected': ({'_field_1': question, '_field_2': answer}), + 'query_parameters': [struct_param], + }, ] for example in EXAMPLES: query = Config.CLIENT.run_sync_query( From 16707a1928f28e75c15bae53d3d5f2aa722c8894 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 20 Dec 2016 17:46:33 -0500 Subject: [PATCH 2/2] Typo. --- bigquery/unit_tests/test__helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bigquery/unit_tests/test__helpers.py b/bigquery/unit_tests/test__helpers.py index 465e5defc143..cc2df7b19006 100644 --- a/bigquery/unit_tests/test__helpers.py +++ b/bigquery/unit_tests/test__helpers.py @@ -1174,7 +1174,7 @@ def test_from_api_repr_w_name(self): RESOURCE = { 'name': 'foo', 'parameterType': { - 'type': 'STRUTCT', + 'type': 'STRUCT', 'structTypes': [ {'name': 'bar', 'type': {'type': 'INT64'}}, {'name': 'baz', 'type': {'type': 'STRING'}}, @@ -1196,7 +1196,7 @@ def test_from_api_repr_w_name(self): def test_from_api_repr_wo_name(self): RESOURCE = { 'parameterType': { - 'type': 'STRUTCT', + 'type': 'STRUCT', 'structTypes': [ {'name': 'bar', 'type': {'type': 'INT64'}}, {'name': 'baz', 'type': {'type': 'STRING'}},