Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/bigquery/query.py: 36%
383 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
1# Copyright 2015 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15"""BigQuery query processing."""
17from collections import OrderedDict
18import copy
19import datetime
20import decimal
21from typing import Any, Optional, Dict, Union
23from google.cloud.bigquery.table import _parse_schema_resource
24from google.cloud.bigquery._helpers import _rows_from_json
25from google.cloud.bigquery._helpers import _QUERY_PARAMS_FROM_JSON
26from google.cloud.bigquery._helpers import _SCALAR_VALUE_TO_JSON_PARAM
29_SCALAR_VALUE_TYPE = Optional[
30 Union[str, int, float, decimal.Decimal, bool, datetime.datetime, datetime.date]
31]
34class ConnectionProperty:
35 """A connection-level property to customize query behavior.
37 See
38 https://cloud.google.com/bigquery/docs/reference/rest/v2/ConnectionProperty
40 Args:
41 key:
42 The key of the property to set, for example, ``'time_zone'`` or
43 ``'session_id'``.
44 value: The value of the property to set.
45 """
47 def __init__(self, key: str = "", value: str = ""):
48 self._properties = {
49 "key": key,
50 "value": value,
51 }
53 @property
54 def key(self) -> str:
55 """Name of the property.
57 For example:
59 * ``time_zone``
60 * ``session_id``
61 """
62 return self._properties["key"]
64 @property
65 def value(self) -> str:
66 """Value of the property."""
67 return self._properties["value"]
69 @classmethod
70 def from_api_repr(cls, resource) -> "ConnectionProperty":
71 """Construct :class:`~google.cloud.bigquery.query.ConnectionProperty`
72 from JSON resource.
74 Args:
75 resource: JSON representation.
77 Returns:
78 A connection property.
79 """
80 value = cls()
81 value._properties = resource
82 return value
84 def to_api_repr(self) -> Dict[str, Any]:
85 """Construct JSON API representation for the connection property.
87 Returns:
88 JSON mapping
89 """
90 return self._properties
93class UDFResource(object):
94 """Describe a single user-defined function (UDF) resource.
96 Args:
97 udf_type (str): The type of the resource ('inlineCode' or 'resourceUri')
99 value (str): The inline code or resource URI.
101 See:
102 https://cloud.google.com/bigquery/user-defined-functions#api
103 """
105 def __init__(self, udf_type, value):
106 self.udf_type = udf_type
107 self.value = value
109 def __eq__(self, other):
110 if not isinstance(other, UDFResource):
111 return NotImplemented
112 return self.udf_type == other.udf_type and self.value == other.value
114 def __ne__(self, other):
115 return not self == other
118class _AbstractQueryParameterType:
119 """Base class for representing query parameter types.
121 https://cloud.google.com/bigquery/docs/reference/rest/v2/QueryParameter#queryparametertype
122 """
124 @classmethod
125 def from_api_repr(cls, resource):
126 """Factory: construct parameter type from JSON resource.
128 Args:
129 resource (Dict): JSON mapping of parameter
131 Returns:
132 google.cloud.bigquery.query.QueryParameterType: Instance
133 """
134 raise NotImplementedError
136 def to_api_repr(self):
137 """Construct JSON API representation for the parameter type.
139 Returns:
140 Dict: JSON mapping
141 """
142 raise NotImplementedError
145class ScalarQueryParameterType(_AbstractQueryParameterType):
146 """Type representation for scalar query parameters.
148 Args:
149 type_ (str):
150 One of 'STRING', 'INT64', 'FLOAT64', 'NUMERIC', 'BOOL', 'TIMESTAMP',
151 'DATETIME', or 'DATE'.
152 name (Optional[str]):
153 The name of the query parameter. Primarily used if the type is
154 one of the subfields in ``StructQueryParameterType`` instance.
155 description (Optional[str]):
156 The query parameter description. Primarily used if the type is
157 one of the subfields in ``StructQueryParameterType`` instance.
158 """
160 def __init__(self, type_, *, name=None, description=None):
161 self._type = type_
162 self.name = name
163 self.description = description
165 @classmethod
166 def from_api_repr(cls, resource):
167 """Factory: construct parameter type from JSON resource.
169 Args:
170 resource (Dict): JSON mapping of parameter
172 Returns:
173 google.cloud.bigquery.query.ScalarQueryParameterType: Instance
174 """
175 type_ = resource["type"]
176 return cls(type_)
178 def to_api_repr(self):
179 """Construct JSON API representation for the parameter type.
181 Returns:
182 Dict: JSON mapping
183 """
184 # Name and description are only used if the type is a field inside a struct
185 # type, but it's StructQueryParameterType's responsibilty to use these two
186 # attributes in the API representation when needed. Here we omit them.
187 return {"type": self._type}
189 def with_name(self, new_name: Union[str, None]):
190 """Return a copy of the instance with ``name`` set to ``new_name``.
192 Args:
193 name (Union[str, None]):
194 The new name of the query parameter type. If ``None``, the existing
195 name is cleared.
197 Returns:
198 google.cloud.bigquery.query.ScalarQueryParameterType:
199 A new instance with updated name.
200 """
201 return type(self)(self._type, name=new_name, description=self.description)
203 def __repr__(self):
204 name = f", name={self.name!r}" if self.name is not None else ""
205 description = (
206 f", description={self.description!r}"
207 if self.description is not None
208 else ""
209 )
210 return f"{self.__class__.__name__}({self._type!r}{name}{description})"
213class ArrayQueryParameterType(_AbstractQueryParameterType):
214 """Type representation for array query parameters.
216 Args:
217 array_type (Union[ScalarQueryParameterType, StructQueryParameterType]):
218 The type of array elements.
219 name (Optional[str]):
220 The name of the query parameter. Primarily used if the type is
221 one of the subfields in ``StructQueryParameterType`` instance.
222 description (Optional[str]):
223 The query parameter description. Primarily used if the type is
224 one of the subfields in ``StructQueryParameterType`` instance.
225 """
227 def __init__(self, array_type, *, name=None, description=None):
228 self._array_type = array_type
229 self.name = name
230 self.description = description
232 @classmethod
233 def from_api_repr(cls, resource):
234 """Factory: construct parameter type from JSON resource.
236 Args:
237 resource (Dict): JSON mapping of parameter
239 Returns:
240 google.cloud.bigquery.query.ArrayQueryParameterType: Instance
241 """
242 array_item_type = resource["arrayType"]["type"]
244 if array_item_type in {"STRUCT", "RECORD"}:
245 klass = StructQueryParameterType
246 else:
247 klass = ScalarQueryParameterType
249 item_type_instance = klass.from_api_repr(resource["arrayType"])
250 return cls(item_type_instance)
252 def to_api_repr(self):
253 """Construct JSON API representation for the parameter type.
255 Returns:
256 Dict: JSON mapping
257 """
258 # Name and description are only used if the type is a field inside a struct
259 # type, but it's StructQueryParameterType's responsibilty to use these two
260 # attributes in the API representation when needed. Here we omit them.
261 return {
262 "type": "ARRAY",
263 "arrayType": self._array_type.to_api_repr(),
264 }
266 def __repr__(self):
267 name = f", name={self.name!r}" if self.name is not None else ""
268 description = (
269 f", description={self.description!r}"
270 if self.description is not None
271 else ""
272 )
273 return f"{self.__class__.__name__}({self._array_type!r}{name}{description})"
276class StructQueryParameterType(_AbstractQueryParameterType):
277 """Type representation for struct query parameters.
279 Args:
280 fields (Iterable[Union[ \
281 ArrayQueryParameterType, ScalarQueryParameterType, StructQueryParameterType \
282 ]]):
283 An non-empty iterable describing the struct's field types.
284 name (Optional[str]):
285 The name of the query parameter. Primarily used if the type is
286 one of the subfields in ``StructQueryParameterType`` instance.
287 description (Optional[str]):
288 The query parameter description. Primarily used if the type is
289 one of the subfields in ``StructQueryParameterType`` instance.
290 """
292 def __init__(self, *fields, name=None, description=None):
293 if not fields:
294 raise ValueError("Struct type must have at least one field defined.")
296 self._fields = fields # fields is a tuple (immutable), no shallow copy needed
297 self.name = name
298 self.description = description
300 @property
301 def fields(self):
302 return self._fields # no copy needed, self._fields is an immutable sequence
304 @classmethod
305 def from_api_repr(cls, resource):
306 """Factory: construct parameter type from JSON resource.
308 Args:
309 resource (Dict): JSON mapping of parameter
311 Returns:
312 google.cloud.bigquery.query.StructQueryParameterType: Instance
313 """
314 fields = []
316 for struct_field in resource["structTypes"]:
317 type_repr = struct_field["type"]
318 if type_repr["type"] in {"STRUCT", "RECORD"}:
319 klass = StructQueryParameterType
320 elif type_repr["type"] == "ARRAY":
321 klass = ArrayQueryParameterType
322 else:
323 klass = ScalarQueryParameterType
325 type_instance = klass.from_api_repr(type_repr)
326 type_instance.name = struct_field.get("name")
327 type_instance.description = struct_field.get("description")
328 fields.append(type_instance)
330 return cls(*fields)
332 def to_api_repr(self):
333 """Construct JSON API representation for the parameter type.
335 Returns:
336 Dict: JSON mapping
337 """
338 fields = []
340 for field in self._fields:
341 item = {"type": field.to_api_repr()}
342 if field.name is not None:
343 item["name"] = field.name
344 if field.description is not None:
345 item["description"] = field.description
347 fields.append(item)
349 return {
350 "type": "STRUCT",
351 "structTypes": fields,
352 }
354 def __repr__(self):
355 name = f", name={self.name!r}" if self.name is not None else ""
356 description = (
357 f", description={self.description!r}"
358 if self.description is not None
359 else ""
360 )
361 items = ", ".join(repr(field) for field in self._fields)
362 return f"{self.__class__.__name__}({items}{name}{description})"
365class _AbstractQueryParameter(object):
366 """Base class for named / positional query parameters."""
368 @classmethod
369 def from_api_repr(cls, resource: dict) -> "_AbstractQueryParameter":
370 """Factory: construct parameter from JSON resource.
372 Args:
373 resource (Dict): JSON mapping of parameter
375 Returns:
376 A new instance of _AbstractQueryParameter subclass.
377 """
378 raise NotImplementedError
380 def to_api_repr(self) -> dict:
381 """Construct JSON API representation for the parameter.
383 Returns:
384 Dict: JSON representation for the parameter.
385 """
386 raise NotImplementedError
389class ScalarQueryParameter(_AbstractQueryParameter):
390 """Named / positional query parameters for scalar values.
392 Args:
393 name:
394 Parameter name, used via ``@foo`` syntax. If None, the
395 parameter can only be addressed via position (``?``).
397 type_:
398 Name of parameter type. See
399 :class:`google.cloud.bigquery.enums.SqlTypeNames` and
400 :class:`google.cloud.bigquery.query.SqlParameterScalarTypes` for
401 supported types.
403 value:
404 The scalar parameter value.
405 """
407 def __init__(
408 self,
409 name: Optional[str],
410 type_: Optional[Union[str, ScalarQueryParameterType]],
411 value: _SCALAR_VALUE_TYPE,
412 ):
413 self.name = name
414 if isinstance(type_, ScalarQueryParameterType):
415 self.type_ = type_._type
416 else:
417 self.type_ = type_
418 self.value = value
420 @classmethod
421 def positional(
422 cls, type_: Union[str, ScalarQueryParameterType], value: _SCALAR_VALUE_TYPE
423 ) -> "ScalarQueryParameter":
424 """Factory for positional paramater.
426 Args:
427 type_:
428 Name of parameter type. One of 'STRING', 'INT64',
429 'FLOAT64', 'NUMERIC', 'BIGNUMERIC', 'BOOL', 'TIMESTAMP', 'DATETIME', or
430 'DATE'.
432 value:
433 The scalar parameter value.
435 Returns:
436 google.cloud.bigquery.query.ScalarQueryParameter: Instance without name
437 """
438 return cls(None, type_, value)
440 @classmethod
441 def from_api_repr(cls, resource: dict) -> "ScalarQueryParameter":
442 """Factory: construct parameter from JSON resource.
444 Args:
445 resource (Dict): JSON mapping of parameter
447 Returns:
448 google.cloud.bigquery.query.ScalarQueryParameter: Instance
449 """
450 name = resource.get("name")
451 type_ = resource["parameterType"]["type"]
453 # parameterValue might not be present if JSON resource originates
454 # from the back-end - the latter omits it for None values.
455 value = resource.get("parameterValue", {}).get("value")
456 if value is not None:
457 converted = _QUERY_PARAMS_FROM_JSON[type_](value, None)
458 else:
459 converted = None
461 return cls(name, type_, converted)
463 def to_api_repr(self) -> dict:
464 """Construct JSON API representation for the parameter.
466 Returns:
467 Dict: JSON mapping
468 """
469 value = self.value
470 converter = _SCALAR_VALUE_TO_JSON_PARAM.get(self.type_)
471 if converter is not None:
472 value = converter(value)
473 resource: Dict[str, Any] = {
474 "parameterType": {"type": self.type_},
475 "parameterValue": {"value": value},
476 }
477 if self.name is not None:
478 resource["name"] = self.name
479 return resource
481 def _key(self):
482 """A tuple key that uniquely describes this field.
484 Used to compute this instance's hashcode and evaluate equality.
486 Returns:
487 Tuple: The contents of this :class:`~google.cloud.bigquery.query.ScalarQueryParameter`.
488 """
489 return (self.name, self.type_.upper(), self.value)
491 def __eq__(self, other):
492 if not isinstance(other, ScalarQueryParameter):
493 return NotImplemented
494 return self._key() == other._key()
496 def __ne__(self, other):
497 return not self == other
499 def __repr__(self):
500 return "ScalarQueryParameter{}".format(self._key())
503class ArrayQueryParameter(_AbstractQueryParameter):
504 """Named / positional query parameters for array values.
506 Args:
507 name (Optional[str]):
508 Parameter name, used via ``@foo`` syntax. If None, the
509 parameter can only be addressed via position (``?``).
511 array_type (Union[str, ScalarQueryParameterType, StructQueryParameterType]):
512 The type of array elements. If given as a string, it must be one of
513 `'STRING'`, `'INT64'`, `'FLOAT64'`, `'NUMERIC'`, `'BIGNUMERIC'`, `'BOOL'`,
514 `'TIMESTAMP'`, `'DATE'`, or `'STRUCT'`/`'RECORD'`.
515 If the type is ``'STRUCT'``/``'RECORD'`` and ``values`` is empty,
516 the exact item type cannot be deduced, thus a ``StructQueryParameterType``
517 instance needs to be passed in.
519 values (List[appropriate type]): The parameter array values.
520 """
522 def __init__(self, name, array_type, values) -> None:
523 self.name = name
524 self.values = values
526 if isinstance(array_type, str):
527 if not values and array_type in {"RECORD", "STRUCT"}:
528 raise ValueError(
529 "Missing detailed struct item type info for an empty array, "
530 "please provide a StructQueryParameterType instance."
531 )
532 self.array_type = array_type
534 @classmethod
535 def positional(cls, array_type: str, values: list) -> "ArrayQueryParameter":
536 """Factory for positional parameters.
538 Args:
539 array_type (Union[str, ScalarQueryParameterType, StructQueryParameterType]):
540 The type of array elements. If given as a string, it must be one of
541 `'STRING'`, `'INT64'`, `'FLOAT64'`, `'NUMERIC'`, `'BIGNUMERIC'`,
542 `'BOOL'`, `'TIMESTAMP'`, `'DATE'`, or `'STRUCT'`/`'RECORD'`.
543 If the type is ``'STRUCT'``/``'RECORD'`` and ``values`` is empty,
544 the exact item type cannot be deduced, thus a ``StructQueryParameterType``
545 instance needs to be passed in.
547 values (List[appropriate type]): The parameter array values.
549 Returns:
550 google.cloud.bigquery.query.ArrayQueryParameter: Instance without name
551 """
552 return cls(None, array_type, values)
554 @classmethod
555 def _from_api_repr_struct(cls, resource):
556 name = resource.get("name")
557 converted = []
558 # We need to flatten the array to use the StructQueryParameter
559 # parse code.
560 resource_template = {
561 # The arrayType includes all the types of the fields of the STRUCT
562 "parameterType": resource["parameterType"]["arrayType"]
563 }
564 for array_value in resource["parameterValue"]["arrayValues"]:
565 struct_resource = copy.deepcopy(resource_template)
566 struct_resource["parameterValue"] = array_value
567 struct_value = StructQueryParameter.from_api_repr(struct_resource)
568 converted.append(struct_value)
569 return cls(name, "STRUCT", converted)
571 @classmethod
572 def _from_api_repr_scalar(cls, resource):
573 name = resource.get("name")
574 array_type = resource["parameterType"]["arrayType"]["type"]
575 parameter_value = resource.get("parameterValue", {})
576 array_values = parameter_value.get("arrayValues", ())
577 values = [value["value"] for value in array_values]
578 converted = [
579 _QUERY_PARAMS_FROM_JSON[array_type](value, None) for value in values
580 ]
581 return cls(name, array_type, converted)
583 @classmethod
584 def from_api_repr(cls, resource: dict) -> "ArrayQueryParameter":
585 """Factory: construct parameter from JSON resource.
587 Args:
588 resource (Dict): JSON mapping of parameter
590 Returns:
591 google.cloud.bigquery.query.ArrayQueryParameter: Instance
592 """
593 array_type = resource["parameterType"]["arrayType"]["type"]
594 if array_type == "STRUCT":
595 return cls._from_api_repr_struct(resource)
596 return cls._from_api_repr_scalar(resource)
598 def to_api_repr(self) -> dict:
599 """Construct JSON API representation for the parameter.
601 Returns:
602 Dict: JSON mapping
603 """
604 values = self.values
606 if self.array_type in {"RECORD", "STRUCT"} or isinstance(
607 self.array_type, StructQueryParameterType
608 ):
609 reprs = [value.to_api_repr() for value in values]
610 a_values = [repr_["parameterValue"] for repr_ in reprs]
612 if reprs:
613 a_type = reprs[0]["parameterType"]
614 else:
615 # This assertion always evaluates to True because the
616 # constructor disallows STRUCT/RECORD type defined as a
617 # string with empty values.
618 assert isinstance(self.array_type, StructQueryParameterType)
619 a_type = self.array_type.to_api_repr()
620 else:
621 # Scalar array item type.
622 if isinstance(self.array_type, str):
623 a_type = {"type": self.array_type}
624 else:
625 a_type = self.array_type.to_api_repr()
627 converter = _SCALAR_VALUE_TO_JSON_PARAM.get(a_type["type"])
628 if converter is not None:
629 values = [converter(value) for value in values]
630 a_values = [{"value": value} for value in values]
632 resource = {
633 "parameterType": {"type": "ARRAY", "arrayType": a_type},
634 "parameterValue": {"arrayValues": a_values},
635 }
636 if self.name is not None:
637 resource["name"] = self.name
639 return resource
641 def _key(self):
642 """A tuple key that uniquely describes this field.
644 Used to compute this instance's hashcode and evaluate equality.
646 Returns:
647 Tuple: The contents of this :class:`~google.cloud.bigquery.query.ArrayQueryParameter`.
648 """
649 if isinstance(self.array_type, str):
650 item_type = self.array_type
651 elif isinstance(self.array_type, ScalarQueryParameterType):
652 item_type = self.array_type._type
653 else:
654 item_type = "STRUCT"
656 return (self.name, item_type.upper(), self.values)
658 def __eq__(self, other):
659 if not isinstance(other, ArrayQueryParameter):
660 return NotImplemented
661 return self._key() == other._key()
663 def __ne__(self, other):
664 return not self == other
666 def __repr__(self):
667 return "ArrayQueryParameter{}".format(self._key())
670class StructQueryParameter(_AbstractQueryParameter):
671 """Named / positional query parameters for struct values.
673 Args:
674 name (Optional[str]):
675 Parameter name, used via ``@foo`` syntax. If None, the
676 parameter can only be addressed via position (``?``).
678 sub_params (Union[Tuple[
679 google.cloud.bigquery.query.ScalarQueryParameter,
680 google.cloud.bigquery.query.ArrayQueryParameter,
681 google.cloud.bigquery.query.StructQueryParameter
682 ]]): The sub-parameters for the struct
683 """
685 def __init__(self, name, *sub_params) -> None:
686 self.name = name
687 self.struct_types: Dict[str, Any] = OrderedDict()
688 self.struct_values: Dict[str, Any] = {}
690 types = self.struct_types
691 values = self.struct_values
692 for sub in sub_params:
693 if isinstance(sub, self.__class__):
694 types[sub.name] = "STRUCT"
695 values[sub.name] = sub
696 elif isinstance(sub, ArrayQueryParameter):
697 types[sub.name] = "ARRAY"
698 values[sub.name] = sub
699 else:
700 types[sub.name] = sub.type_
701 values[sub.name] = sub.value
703 @classmethod
704 def positional(cls, *sub_params):
705 """Factory for positional parameters.
707 Args:
708 sub_params (Union[Tuple[
709 google.cloud.bigquery.query.ScalarQueryParameter,
710 google.cloud.bigquery.query.ArrayQueryParameter,
711 google.cloud.bigquery.query.StructQueryParameter
712 ]]): The sub-parameters for the struct
714 Returns:
715 google.cloud.bigquery.query.StructQueryParameter: Instance without name
716 """
717 return cls(None, *sub_params)
719 @classmethod
720 def from_api_repr(cls, resource: dict) -> "StructQueryParameter":
721 """Factory: construct parameter from JSON resource.
723 Args:
724 resource (Dict): JSON mapping of parameter
726 Returns:
727 google.cloud.bigquery.query.StructQueryParameter: Instance
728 """
729 name = resource.get("name")
730 instance = cls(name)
731 type_resources = {}
732 types = instance.struct_types
733 for item in resource["parameterType"]["structTypes"]:
734 types[item["name"]] = item["type"]["type"]
735 type_resources[item["name"]] = item["type"]
736 struct_values = resource["parameterValue"]["structValues"]
737 for key, value in struct_values.items():
738 type_ = types[key]
739 converted: Optional[Union[ArrayQueryParameter, StructQueryParameter]] = None
740 if type_ == "STRUCT":
741 struct_resource = {
742 "name": key,
743 "parameterType": type_resources[key],
744 "parameterValue": value,
745 }
746 converted = StructQueryParameter.from_api_repr(struct_resource)
747 elif type_ == "ARRAY":
748 struct_resource = {
749 "name": key,
750 "parameterType": type_resources[key],
751 "parameterValue": value,
752 }
753 converted = ArrayQueryParameter.from_api_repr(struct_resource)
754 else:
755 value = value["value"]
756 converted = _QUERY_PARAMS_FROM_JSON[type_](value, None)
757 instance.struct_values[key] = converted
758 return instance
760 def to_api_repr(self) -> dict:
761 """Construct JSON API representation for the parameter.
763 Returns:
764 Dict: JSON mapping
765 """
766 s_types = {}
767 values = {}
768 for name, value in self.struct_values.items():
769 type_ = self.struct_types[name]
770 if type_ in ("STRUCT", "ARRAY"):
771 repr_ = value.to_api_repr()
772 s_types[name] = {"name": name, "type": repr_["parameterType"]}
773 values[name] = repr_["parameterValue"]
774 else:
775 s_types[name] = {"name": name, "type": {"type": type_}}
776 converter = _SCALAR_VALUE_TO_JSON_PARAM.get(type_)
777 if converter is not None:
778 value = converter(value)
779 values[name] = {"value": value}
781 resource = {
782 "parameterType": {
783 "type": "STRUCT",
784 "structTypes": [s_types[key] for key in self.struct_types],
785 },
786 "parameterValue": {"structValues": values},
787 }
788 if self.name is not None:
789 resource["name"] = self.name
790 return resource
792 def _key(self):
793 """A tuple key that uniquely describes this field.
795 Used to compute this instance's hashcode and evaluate equality.
797 Returns:
798 Tuple: The contents of this :class:`~google.cloud.bigquery.ArrayQueryParameter`.
799 """
800 return (self.name, self.struct_types, self.struct_values)
802 def __eq__(self, other):
803 if not isinstance(other, StructQueryParameter):
804 return NotImplemented
805 return self._key() == other._key()
807 def __ne__(self, other):
808 return not self == other
810 def __repr__(self):
811 return "StructQueryParameter{}".format(self._key())
814class SqlParameterScalarTypes:
815 """Supported scalar SQL query parameter types as type objects."""
817 BOOL = ScalarQueryParameterType("BOOL")
818 BOOLEAN = ScalarQueryParameterType("BOOL")
819 BIGDECIMAL = ScalarQueryParameterType("BIGNUMERIC")
820 BIGNUMERIC = ScalarQueryParameterType("BIGNUMERIC")
821 BYTES = ScalarQueryParameterType("BYTES")
822 DATE = ScalarQueryParameterType("DATE")
823 DATETIME = ScalarQueryParameterType("DATETIME")
824 DECIMAL = ScalarQueryParameterType("NUMERIC")
825 FLOAT = ScalarQueryParameterType("FLOAT64")
826 FLOAT64 = ScalarQueryParameterType("FLOAT64")
827 GEOGRAPHY = ScalarQueryParameterType("GEOGRAPHY")
828 INT64 = ScalarQueryParameterType("INT64")
829 INTEGER = ScalarQueryParameterType("INT64")
830 NUMERIC = ScalarQueryParameterType("NUMERIC")
831 STRING = ScalarQueryParameterType("STRING")
832 TIME = ScalarQueryParameterType("TIME")
833 TIMESTAMP = ScalarQueryParameterType("TIMESTAMP")
836class _QueryResults(object):
837 """Results of a query.
839 See:
840 https://g.co/cloud/bigquery/docs/reference/rest/v2/jobs/getQueryResults
841 """
843 def __init__(self, properties):
844 self._properties = {}
845 self._set_properties(properties)
847 @classmethod
848 def from_api_repr(cls, api_response):
849 return cls(api_response)
851 @property
852 def project(self):
853 """Project bound to the query job.
855 Returns:
856 str: The project that the query job is associated with.
857 """
858 return self._properties.get("jobReference", {}).get("projectId")
860 @property
861 def cache_hit(self):
862 """Query results served from cache.
864 See:
865 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.cache_hit
867 Returns:
868 Optional[bool]:
869 True if the query results were served from cache (None
870 until set by the server).
871 """
872 return self._properties.get("cacheHit")
874 @property
875 def complete(self):
876 """Server completed query.
878 See:
879 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.job_complete
881 Returns:
882 Optional[bool]:
883 True if the query completed on the server (None
884 until set by the server).
885 """
886 return self._properties.get("jobComplete")
888 @property
889 def errors(self):
890 """Errors generated by the query.
892 See:
893 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.errors
895 Returns:
896 Optional[List[Mapping]]:
897 Mappings describing errors generated on the server (None
898 until set by the server).
899 """
900 return self._properties.get("errors")
902 @property
903 def job_id(self):
904 """Job ID of the query job these results are from.
906 See:
907 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.job_reference
909 Returns:
910 str: Job ID of the query job.
911 """
912 return self._properties.get("jobReference", {}).get("jobId")
914 @property
915 def page_token(self):
916 """Token for fetching next bach of results.
918 See:
919 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.page_token
921 Returns:
922 Optional[str]: Token generated on the server (None until set by the server).
923 """
924 return self._properties.get("pageToken")
926 @property
927 def total_rows(self):
928 """Total number of rows returned by the query.
930 See:
931 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.total_rows
933 Returns:
934 Optional[int]: Count generated on the server (None until set by the server).
935 """
936 total_rows = self._properties.get("totalRows")
937 if total_rows is not None:
938 return int(total_rows)
940 @property
941 def total_bytes_processed(self):
942 """Total number of bytes processed by the query.
944 See:
945 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.total_bytes_processed
947 Returns:
948 Optional[int]: Count generated on the server (None until set by the server).
949 """
950 total_bytes_processed = self._properties.get("totalBytesProcessed")
951 if total_bytes_processed is not None:
952 return int(total_bytes_processed)
954 @property
955 def num_dml_affected_rows(self):
956 """Total number of rows affected by a DML query.
958 See:
959 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.num_dml_affected_rows
961 Returns:
962 Optional[int]: Count generated on the server (None until set by the server).
963 """
964 num_dml_affected_rows = self._properties.get("numDmlAffectedRows")
965 if num_dml_affected_rows is not None:
966 return int(num_dml_affected_rows)
968 @property
969 def rows(self):
970 """Query results.
972 See:
973 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.rows
975 Returns:
976 Optional[List[google.cloud.bigquery.table.Row]]:
977 Rows containing the results of the query.
978 """
979 return _rows_from_json(self._properties.get("rows", ()), self.schema)
981 @property
982 def schema(self):
983 """Schema for query results.
985 See:
986 https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.schema
988 Returns:
989 Optional[List[SchemaField]]:
990 Fields describing the schema (None until set by the server).
991 """
992 return _parse_schema_resource(self._properties.get("schema", {}))
994 def _set_properties(self, api_response):
995 """Update properties from resource in body of ``api_response``
997 Args:
998 api_response (Dict): Response returned from an API call
999 """
1000 job_id_present = (
1001 "jobReference" in api_response
1002 and "jobId" in api_response["jobReference"]
1003 and "projectId" in api_response["jobReference"]
1004 )
1005 if not job_id_present:
1006 raise ValueError("QueryResult requires a job reference")
1008 self._properties.clear()
1009 self._properties.update(copy.deepcopy(api_response))
1012def _query_param_from_api_repr(resource):
1013 """Helper: Construct concrete query parameter from JSON resource."""
1014 qp_type = resource["parameterType"]
1015 if "arrayType" in qp_type:
1016 klass = ArrayQueryParameter
1017 elif "structTypes" in qp_type:
1018 klass = StructQueryParameter
1019 else:
1020 klass = ScalarQueryParameter
1021 return klass.from_api_repr(resource)