@@ -43,27 +43,9 @@ def scalar_to_query_parameter(value, name=None):
4343 :raises: :class:`~google.cloud.bigquery.dbapi.exceptions.ProgrammingError`
4444 if the type cannot be determined.
4545 """
46- parameter_type = None
46+ parameter_type = bigquery_scalar_type ( value )
4747
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 :
6749 raise exceptions .ProgrammingError (
6850 "encountered parameter {} with value {} of unexpected type" .format (
6951 name , value
@@ -72,6 +54,46 @@ def scalar_to_query_parameter(value, name=None):
7254 return bigquery .ScalarQueryParameter (name , parameter_type , value )
7355
7456
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+
7597def to_query_parameters_list (parameters ):
7698 """Converts a sequence of parameter values into query parameters.
7799
@@ -81,7 +103,18 @@ def to_query_parameters_list(parameters):
81103 :rtype: List[google.cloud.bigquery.query._AbstractQueryParameter]
82104 :returns: A list of query parameters.
83105 """
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
85118
86119
87120def to_query_parameters_dict (parameters ):
@@ -93,10 +126,21 @@ def to_query_parameters_dict(parameters):
93126 :rtype: List[google.cloud.bigquery.query._AbstractQueryParameter]
94127 :returns: A list of named query parameters.
95128 """
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
100144
101145
102146def to_query_parameters (parameters ):
@@ -115,3 +159,55 @@ def to_query_parameters(parameters):
115159 return to_query_parameters_dict (parameters )
116160
117161 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