2020import numbers
2121
2222from google .cloud import bigquery
23- from google .cloud .bigquery import table
23+ from google .cloud .bigquery import table , enums
2424from google .cloud .bigquery .dbapi import exceptions
2525
2626
2727_NUMERIC_SERVER_MIN = decimal .Decimal ("-9.9999999999999999999999999999999999999E+28" )
2828_NUMERIC_SERVER_MAX = decimal .Decimal ("9.9999999999999999999999999999999999999E+28" )
2929
3030
31- def scalar_to_query_parameter (value ,name = None ):
31+ def _parameter_type (name ,value ,query_parameter_type = None ,value_doc = "" ):
32+ if query_parameter_type :
33+ try :
34+ parameter_type = getattr (
35+ enums .SqlParameterScalarTypes ,query_parameter_type .upper ()
36+ )._type
37+ except AttributeError :
38+ raise exceptions .ProgrammingError (
39+ f"The given parameter type,{ query_parameter_type } ,"
40+ f" for{ name } is not a valid BigQuery scalar type."
41+ )
42+ else :
43+ parameter_type = bigquery_scalar_type (value )
44+ if parameter_type is None :
45+ raise exceptions .ProgrammingError (
46+ f"Encountered parameter{ name } with "
47+ f"{ value_doc } value{ value } of unexpected type."
48+ )
49+ return parameter_type
50+
51+
52+ def scalar_to_query_parameter (value ,name = None ,query_parameter_type = None ):
3253"""Convert a scalar value into a query parameter.
3354
3455 Args:
@@ -37,6 +58,7 @@ def scalar_to_query_parameter(value, name=None):
3758
3859 name (str):
3960 (Optional) Name of the query parameter.
61+ query_parameter_type (Optional[str]): Given type for the parameter.
4062
4163 Returns:
4264 google.cloud.bigquery.ScalarQueryParameter:
@@ -47,24 +69,19 @@ def scalar_to_query_parameter(value, name=None):
4769 google.cloud.bigquery.dbapi.exceptions.ProgrammingError:
4870 if the type cannot be determined.
4971 """
50- parameter_type = bigquery_scalar_type (value )
51-
52- if parameter_type is None :
53- raise exceptions .ProgrammingError (
54- "encountered parameter {} with value {} of unexpected type" .format (
55- name ,value
56- )
57- )
58- return bigquery .ScalarQueryParameter (name ,parameter_type ,value )
72+ return bigquery .ScalarQueryParameter (
73+ name ,_parameter_type (name ,value ,query_parameter_type ),value
74+ )
5975
6076
61- def array_to_query_parameter (value ,name = None ):
77+ def array_to_query_parameter (value ,name = None , query_parameter_type = None ):
6278"""Convert an array-like value into a query parameter.
6379
6480 Args:
6581 value (Sequence[Any]): The elements of the array (should not be a
6682 string-like Sequence).
6783 name (Optional[str]): Name of the query parameter.
84+ query_parameter_type (Optional[str]): Given type for the parameter.
6885
6986 Returns:
7087 A query parameter corresponding with the type and value of the plain
@@ -80,53 +97,58 @@ def array_to_query_parameter(value, name=None):
8097"not string-like." .format (name )
8198 )
8299
83- if not value :
100+ if query_parameter_type or value :
101+ array_type = _parameter_type (
102+ name ,
103+ value [0 ]if value else None ,
104+ query_parameter_type ,
105+ value_doc = "array element " ,
106+ )
107+ else :
84108raise exceptions .ProgrammingError (
85109"Encountered an empty array-like value of parameter {}, cannot "
86110"determine array elements type." .format (name )
87111 )
88112
89- # Assume that all elements are of the same type, and let the backend handle
90- # any type incompatibilities among the array elements
91- array_type = bigquery_scalar_type (value [0 ])
92- if array_type is None :
93- raise exceptions .ProgrammingError (
94- "Encountered unexpected first array element of parameter {}, "
95- "cannot determine array elements type." .format (name )
96- )
97-
98113return bigquery .ArrayQueryParameter (name ,array_type ,value )
99114
100115
101- def to_query_parameters_list (parameters ):
116+ def to_query_parameters_list (parameters , parameter_types ):
102117"""Converts a sequence of parameter values into query parameters.
103118
104119 Args:
105120 parameters (Sequence[Any]): Sequence of query parameter values.
121+ parameter_types:
122+ A list of parameter types, one for each parameter.
123+ Unknown types are provided as None.
106124
107125 Returns:
108126 List[google.cloud.bigquery.query._AbstractQueryParameter]:
109127 A list of query parameters.
110128 """
111129result = []
112130
113- for value in parameters :
131+ for value , type_ in zip ( parameters , parameter_types ) :
114132if isinstance (value ,collections_abc .Mapping ):
115133raise NotImplementedError ("STRUCT-like parameter values are not supported." )
116134elif array_like (value ):
117- param = array_to_query_parameter (value )
135+ param = array_to_query_parameter (value , None , type_ )
118136else :
119- param = scalar_to_query_parameter (value )
137+ param = scalar_to_query_parameter (value ,None ,type_ )
138+
120139result .append (param )
121140
122141return result
123142
124143
125- def to_query_parameters_dict (parameters ):
144+ def to_query_parameters_dict (parameters , query_parameter_types ):
126145"""Converts a dictionary of parameter values into query parameters.
127146
128147 Args:
129148 parameters (Mapping[str, Any]): Dictionary of query parameter values.
149+ parameter_types:
150+ A dictionary of parameter types. It needn't have a key for each
151+ parameter.
130152
131153 Returns:
132154 List[google.cloud.bigquery.query._AbstractQueryParameter]:
@@ -140,21 +162,38 @@ def to_query_parameters_dict(parameters):
140162"STRUCT-like parameter values are not supported "
141163"(parameter {})." .format (name )
142164 )
143- elif array_like (value ):
144- param = array_to_query_parameter (value ,name = name )
145165else :
146- param = scalar_to_query_parameter (value ,name = name )
166+ query_parameter_type = query_parameter_types .get (name )
167+ if array_like (value ):
168+ param = array_to_query_parameter (
169+ value ,name = name ,query_parameter_type = query_parameter_type
170+ )
171+ else :
172+ param = scalar_to_query_parameter (
173+ value ,name = name ,query_parameter_type = query_parameter_type ,
174+ )
175+
147176result .append (param )
148177
149178return result
150179
151180
152- def to_query_parameters (parameters ):
181+ def to_query_parameters (parameters , parameter_types ):
153182"""Converts DB-API parameter values into query parameters.
154183
155184 Args:
156185 parameters (Union[Mapping[str, Any], Sequence[Any]]):
157186 A dictionary or sequence of query parameter values.
187+ parameter_types (Union[Mapping[str, str], Sequence[str]]):
188+ A dictionary or list of parameter types.
189+
190+ If parameters is a mapping, then this must be a dictionary
191+ of parameter types. It needn't have a key for each
192+ parameter.
193+
194+ If parameters is a sequence, then this must be a list of
195+ parameter types, one for each paramater. Unknown types
196+ are provided as None.
158197
159198 Returns:
160199 List[google.cloud.bigquery.query._AbstractQueryParameter]:
@@ -164,9 +203,9 @@ def to_query_parameters(parameters):
164203return []
165204
166205if isinstance (parameters ,collections_abc .Mapping ):
167- return to_query_parameters_dict (parameters )
168-
169- return to_query_parameters_list (parameters )
206+ return to_query_parameters_dict (parameters , parameter_types )
207+ else :
208+ return to_query_parameters_list (parameters , parameter_types )
170209
171210
172211def bigquery_scalar_type (value ):