@@ -43,27 +43,9 @@ def scalar_to_query_parameter(value, name=None):
43
43
:raises: :class:`~google.cloud.bigquery.dbapi.exceptions.ProgrammingError`
44
44
if the type cannot be determined.
45
45
"""
46
- parameter_type = None
46
+ parameter_type = bigquery_scalar_type ( value )
47
47
48
- if isinstance (value , bool ):
49
- parameter_type = "BOOL"
50
- elif isinstance (value , numbers .Integral ):
51
- parameter_type = "INT64"
52
- elif isinstance (value , numbers .Real ):
53
- parameter_type = "FLOAT64"
54
- elif isinstance (value , decimal .Decimal ):
55
- parameter_type = "NUMERIC"
56
- elif isinstance (value , six .text_type ):
57
- parameter_type = "STRING"
58
- elif isinstance (value , six .binary_type ):
59
- parameter_type = "BYTES"
60
- elif isinstance (value , datetime .datetime ):
61
- parameter_type = "DATETIME" if value .tzinfo is None else "TIMESTAMP"
62
- elif isinstance (value , datetime .date ):
63
- parameter_type = "DATE"
64
- elif isinstance (value , datetime .time ):
65
- parameter_type = "TIME"
66
- else :
48
+ if parameter_type is None :
67
49
raise exceptions .ProgrammingError (
68
50
"encountered parameter {} with value {} of unexpected type" .format (
69
51
name , value
@@ -72,6 +54,46 @@ def scalar_to_query_parameter(value, name=None):
72
54
return bigquery .ScalarQueryParameter (name , parameter_type , value )
73
55
74
56
57
+ def array_to_query_parameter (value , name = None ):
58
+ """Convert an array-like value into a query parameter.
59
+
60
+ Args:
61
+ value (Sequence[Any]): The elements of the array (should not be a
62
+ string-like Sequence).
63
+ name (Optional[str]): Name of the query parameter.
64
+
65
+ Returns:
66
+ A query parameter corresponding with the type and value of the plain
67
+ Python object.
68
+
69
+ Raises:
70
+ :class:`~google.cloud.bigquery.dbapi.exceptions.ProgrammingError`
71
+ if the type of array elements cannot be determined.
72
+ """
73
+ if not array_like (value ):
74
+ raise exceptions .ProgrammingError (
75
+ "The value of parameter {} must be a sequence that is "
76
+ "not string-like." .format (name )
77
+ )
78
+
79
+ if not value :
80
+ raise exceptions .ProgrammingError (
81
+ "Encountered an empty array-like value of parameter {}, cannot "
82
+ "determine array elements type." .format (name )
83
+ )
84
+
85
+ # Assume that all elements are of the same type, and let the backend handle
86
+ # any type incompatibilities among the array elements
87
+ array_type = bigquery_scalar_type (value [0 ])
88
+ if array_type is None :
89
+ raise exceptions .ProgrammingError (
90
+ "Encountered unexpected first array element of parameter {}, "
91
+ "cannot determine array elements type." .format (name )
92
+ )
93
+
94
+ return bigquery .ArrayQueryParameter (name , array_type , value )
95
+
96
+
75
97
def to_query_parameters_list (parameters ):
76
98
"""Converts a sequence of parameter values into query parameters.
77
99
@@ -81,7 +103,18 @@ def to_query_parameters_list(parameters):
81
103
:rtype: List[google.cloud.bigquery.query._AbstractQueryParameter]
82
104
:returns: A list of query parameters.
83
105
"""
84
- return [scalar_to_query_parameter (value ) for value in parameters ]
106
+ result = []
107
+
108
+ for value in parameters :
109
+ if isinstance (value , collections_abc .Mapping ):
110
+ raise NotImplementedError ("STRUCT-like parameter values are not supported." )
111
+ elif array_like (value ):
112
+ param = array_to_query_parameter (value )
113
+ else :
114
+ param = scalar_to_query_parameter (value )
115
+ result .append (param )
116
+
117
+ return result
85
118
86
119
87
120
def to_query_parameters_dict (parameters ):
@@ -93,10 +126,21 @@ def to_query_parameters_dict(parameters):
93
126
:rtype: List[google.cloud.bigquery.query._AbstractQueryParameter]
94
127
:returns: A list of named query parameters.
95
128
"""
96
- return [
97
- scalar_to_query_parameter (value , name = name )
98
- for name , value in six .iteritems (parameters )
99
- ]
129
+ result = []
130
+
131
+ for name , value in six .iteritems (parameters ):
132
+ if isinstance (value , collections_abc .Mapping ):
133
+ raise NotImplementedError (
134
+ "STRUCT-like parameter values are not supported "
135
+ "(parameter {})." .format (name )
136
+ )
137
+ elif array_like (value ):
138
+ param = array_to_query_parameter (value , name = name )
139
+ else :
140
+ param = scalar_to_query_parameter (value , name = name )
141
+ result .append (param )
142
+
143
+ return result
100
144
101
145
102
146
def to_query_parameters (parameters ):
@@ -115,3 +159,55 @@ def to_query_parameters(parameters):
115
159
return to_query_parameters_dict (parameters )
116
160
117
161
return to_query_parameters_list (parameters )
162
+
163
+
164
+ def bigquery_scalar_type (value ):
165
+ """Return a BigQuery name of the scalar type that matches the given value.
166
+
167
+ If the scalar type name could not be determined (e.g. for non-scalar
168
+ values), ``None`` is returned.
169
+
170
+ Args:
171
+ value (Any)
172
+
173
+ Returns:
174
+ Optional[str]: The BigQuery scalar type name.
175
+ """
176
+ if isinstance (value , bool ):
177
+ return "BOOL"
178
+ elif isinstance (value , numbers .Integral ):
179
+ return "INT64"
180
+ elif isinstance (value , numbers .Real ):
181
+ return "FLOAT64"
182
+ elif isinstance (value , decimal .Decimal ):
183
+ return "NUMERIC"
184
+ elif isinstance (value , six .text_type ):
185
+ return "STRING"
186
+ elif isinstance (value , six .binary_type ):
187
+ return "BYTES"
188
+ elif isinstance (value , datetime .datetime ):
189
+ return "DATETIME" if value .tzinfo is None else "TIMESTAMP"
190
+ elif isinstance (value , datetime .date ):
191
+ return "DATE"
192
+ elif isinstance (value , datetime .time ):
193
+ return "TIME"
194
+
195
+ return None
196
+
197
+
198
+ def array_like (value ):
199
+ """Determine if the given value is array-like.
200
+
201
+ Examples of array-like values (as interpreted by this function) are
202
+ sequences such as ``list`` and ``tuple``, but not strings and other
203
+ iterables such as sets.
204
+
205
+ Args:
206
+ value (Any)
207
+
208
+ Returns:
209
+ bool: ``True`` if the value is considered array-like, ``False`` otherwise.
210
+ """
211
+ return isinstance (value , collections_abc .Sequence ) and not isinstance (
212
+ value , (six .text_type , six .binary_type , bytearray )
213
+ )
0 commit comments