Skip to content

Commit bfb4060

Browse files
committed
Conform to expected wire format for struct query parameters.
Closes: #2887.
1 parent a8fb997 commit bfb4060

File tree

3 files changed

+54
-16
lines changed

3 files changed

+54
-16
lines changed

bigquery/google/cloud/bigquery/_helpers.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ def to_api_repr(self):
501501
values = [converter(value) for value in values]
502502
resource = {
503503
'parameterType': {
504+
'type': 'ARRAY',
504505
'arrayType': self.array_type,
505506
},
506507
'parameterValue': {
@@ -554,10 +555,12 @@ def from_api_repr(cls, resource):
554555
instance = cls(name)
555556
types = instance.struct_types
556557
for item in resource['parameterType']['structTypes']:
557-
types[item['name']] = item['type']
558+
types[item['name']] = item['type']['type']
558559
struct_values = resource['parameterValue']['structValues']
559560
for key, value in struct_values.items():
560-
converted = _CELLDATA_FROM_JSON[types[key]](value, None)
561+
type_ = types[key]
562+
value = value['value']
563+
converted = _CELLDATA_FROM_JSON[type_](value, None)
561564
instance.struct_values[key] = converted
562565
return instance
563566

@@ -568,18 +571,19 @@ def to_api_repr(self):
568571
:returns: JSON mapping
569572
"""
570573
types = [
571-
{'name': key, 'type': value}
574+
{'name': key, 'type': {'type': value}}
572575
for key, value in self.struct_types.items()
573576
]
574577
values = {}
575578
for name, value in self.struct_values.items():
576579
converter = _SCALAR_VALUE_TO_JSON.get(self.struct_types[name])
577580
if converter is not None:
578581
value = converter(value)
579-
values[name] = value
582+
values[name] = {'value': value}
580583

581584
resource = {
582585
'parameterType': {
586+
'type': 'STRUCT',
583587
'structTypes': types,
584588
},
585589
'parameterValue': {

bigquery/unit_tests/test__helpers.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,7 @@ def test_from_api_repr_w_name(self):
10821082
def test_from_api_repr_wo_name(self):
10831083
RESOURCE = {
10841084
'parameterType': {
1085+
'type': 'ARRAY',
10851086
'arrayType': 'INT64',
10861087
},
10871088
'parameterValue': {
@@ -1098,6 +1099,7 @@ def test_to_api_repr_w_name(self):
10981099
EXPECTED = {
10991100
'name': 'foo',
11001101
'parameterType': {
1102+
'type': 'ARRAY',
11011103
'arrayType': 'INT64',
11021104
},
11031105
'parameterValue': {
@@ -1110,6 +1112,7 @@ def test_to_api_repr_w_name(self):
11101112
def test_to_api_repr_wo_name(self):
11111113
EXPECTED = {
11121114
'parameterType': {
1115+
'type': 'ARRAY',
11131116
'arrayType': 'INT64',
11141117
},
11151118
'parameterValue': {
@@ -1123,6 +1126,7 @@ def test_to_api_repr_wo_name(self):
11231126
def test_to_api_repr_w_unknown_type(self):
11241127
EXPECTED = {
11251128
'parameterType': {
1129+
'type': 'ARRAY',
11261130
'arrayType': 'UNKNOWN',
11271131
},
11281132
'parameterValue': {
@@ -1170,13 +1174,17 @@ def test_from_api_repr_w_name(self):
11701174
RESOURCE = {
11711175
'name': 'foo',
11721176
'parameterType': {
1177+
'type': 'STRUTCT',
11731178
'structTypes': [
1174-
{'name': 'bar', 'type': 'INT64'},
1175-
{'name': 'baz', 'type': 'STRING'},
1179+
{'name': 'bar', 'type': {'type': 'INT64'}},
1180+
{'name': 'baz', 'type': {'type': 'STRING'}},
11761181
],
11771182
},
11781183
'parameterValue': {
1179-
'structValues': {'bar': 123, 'baz': 'abc'},
1184+
'structValues': {
1185+
'bar': {'value': 123},
1186+
'baz': {'value': 'abc'},
1187+
},
11801188
},
11811189
}
11821190
klass = self._get_target_class()
@@ -1188,13 +1196,17 @@ def test_from_api_repr_w_name(self):
11881196
def test_from_api_repr_wo_name(self):
11891197
RESOURCE = {
11901198
'parameterType': {
1199+
'type': 'STRUTCT',
11911200
'structTypes': [
1192-
{'name': 'bar', 'type': 'INT64'},
1193-
{'name': 'baz', 'type': 'STRING'},
1201+
{'name': 'bar', 'type': {'type': 'INT64'}},
1202+
{'name': 'baz', 'type': {'type': 'STRING'}},
11941203
],
11951204
},
11961205
'parameterValue': {
1197-
'structValues': {'bar': 123, 'baz': 'abc'},
1206+
'structValues': {
1207+
'bar': {'value': 123},
1208+
'baz': {'value': 'abc'},
1209+
},
11981210
},
11991211
}
12001212
klass = self._get_target_class()
@@ -1207,13 +1219,17 @@ def test_to_api_repr_w_name(self):
12071219
EXPECTED = {
12081220
'name': 'foo',
12091221
'parameterType': {
1222+
'type': 'STRUCT',
12101223
'structTypes': [
1211-
{'name': 'bar', 'type': 'INT64'},
1212-
{'name': 'baz', 'type': 'STRING'},
1224+
{'name': 'bar', 'type': {'type': 'INT64'}},
1225+
{'name': 'baz', 'type': {'type': 'STRING'}},
12131226
],
12141227
},
12151228
'parameterValue': {
1216-
'structValues': {'bar': '123', 'baz': 'abc'},
1229+
'structValues': {
1230+
'bar': {'value': '123'},
1231+
'baz': {'value': 'abc'},
1232+
},
12171233
},
12181234
}
12191235
sub_1 = self._make_subparam('bar', 'INT64', 123)
@@ -1224,13 +1240,17 @@ def test_to_api_repr_w_name(self):
12241240
def test_to_api_repr_wo_name(self):
12251241
EXPECTED = {
12261242
'parameterType': {
1243+
'type': 'STRUCT',
12271244
'structTypes': [
1228-
{'name': 'bar', 'type': 'INT64'},
1229-
{'name': 'baz', 'type': 'STRING'},
1245+
{'name': 'bar', 'type': {'type': 'INT64'}},
1246+
{'name': 'baz', 'type': {'type': 'STRING'}},
12301247
],
12311248
},
12321249
'parameterValue': {
1233-
'structValues': {'bar': '123', 'baz': 'abc'},
1250+
'structValues': {
1251+
'bar': {'value': '123'},
1252+
'baz': {'value': 'abc'},
1253+
},
12341254
},
12351255
}
12361256
sub_1 = self._make_subparam('bar', 'INT64', 123)

system_tests/bigquery.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,11 +483,20 @@ def test_sync_query_w_standard_sql_types(self):
483483
import datetime
484484
from google.cloud._helpers import UTC
485485
from google.cloud.bigquery._helpers import ScalarQueryParameter
486+
from google.cloud.bigquery._helpers import StructQueryParameter
486487
naive = datetime.datetime(2016, 12, 5, 12, 41, 9)
487488
stamp = "%s %s" % (naive.date().isoformat(), naive.time().isoformat())
488489
zoned = naive.replace(tzinfo=UTC)
489490
zoned_param = ScalarQueryParameter(
490491
name='zoned', type_='TIMESTAMP', value=zoned)
492+
question = 'What is the answer to life, the universe, and everything?'
493+
question_param = ScalarQueryParameter(
494+
name='question', type_='STRING', value=question)
495+
answer = 42
496+
answer_param = ScalarQueryParameter(
497+
name='answer', type_='INT64', value=answer)
498+
struct_param = StructQueryParameter(
499+
'hitchhiker', question_param, answer_param)
491500
EXAMPLES = [
492501
{
493502
'sql': 'SELECT 1',
@@ -561,6 +570,11 @@ def test_sync_query_w_standard_sql_types(self):
561570
'expected': zoned,
562571
'query_parameters': [zoned_param],
563572
},
573+
{
574+
'sql': 'SELECT (@hitchhiker.question, @hitchhiker.answer)',
575+
'expected': ({'_field_1': question, '_field_2': answer}),
576+
'query_parameters': [struct_param],
577+
},
564578
]
565579
for example in EXAMPLES:
566580
query = Config.CLIENT.run_sync_query(

0 commit comments

Comments
 (0)