Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: DB API depends on pyarrow when decimal query parameters are used #551

Merged
merged 3 commits into from Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions google/cloud/bigquery/dbapi/_helpers.py
Expand Up @@ -189,6 +189,10 @@ def bigquery_scalar_type(value):
elif isinstance(value, numbers.Real):
return "FLOAT64"
elif isinstance(value, decimal.Decimal):
if pyarrow is None:
# We don't try to manually guess if it's maybe a BIGNUMERIC.
return "NUMERIC"
plamut marked this conversation as resolved.
Show resolved Hide resolved

# We check for NUMERIC before BIGNUMERIC in order to support pyarrow < 3.0.
scalar_object = pyarrow.scalar(value)
if isinstance(scalar_object, pyarrow.Decimal128Scalar):
Expand Down
37 changes: 30 additions & 7 deletions tests/unit/test_dbapi__helpers.py
Expand Up @@ -17,6 +17,7 @@
import math
import operator as op
import unittest
from unittest import mock

try:
import pyarrow
Expand All @@ -41,7 +42,7 @@ def test_scalar_to_query_parameter(self):
(1.25, "FLOAT64"),
(decimal.Decimal("1.25"), "NUMERIC"),
(b"I am some bytes", "BYTES"),
(u"I am a string", "STRING"),
("I am a string", "STRING"),
(datetime.date(2017, 4, 1), "DATE"),
(datetime.time(12, 34, 56), "TIME"),
(datetime.datetime(2012, 3, 4, 5, 6, 7), "DATETIME"),
Expand Down Expand Up @@ -71,6 +72,28 @@ def test_scalar_to_query_parameter(self):
self.assertEqual(named_parameter.type_, expected_type, msg=msg)
self.assertEqual(named_parameter.value, value, msg=msg)

def test_decimal_to_query_parameter_no_pyarrow(self):
# The value should have a high-enough scale to be recognized as BIGNUMERIC
# if pyarrow was available.
value = decimal.Decimal("1.1234567890123456789012345678901234567890")
msg = f"value: {value} expected_type: NUMERIC"

patcher = mock.patch("google.cloud.bigquery.dbapi._helpers.pyarrow", new=None)

with patcher:
parameter = _helpers.scalar_to_query_parameter(value)

self.assertIsNone(parameter.name, msg=msg)
self.assertEqual(parameter.type_, "NUMERIC", msg=msg)
self.assertEqual(parameter.value, value, msg=msg)

with patcher:
named_parameter = _helpers.scalar_to_query_parameter(value, name="myvar")

self.assertEqual(named_parameter.name, "myvar", msg=msg)
self.assertEqual(named_parameter.type_, "NUMERIC", msg=msg)
self.assertEqual(named_parameter.value, value, msg=msg)

def test_scalar_to_query_parameter_w_unexpected_type(self):
with self.assertRaises(exceptions.ProgrammingError):
_helpers.scalar_to_query_parameter(value={"a": "dictionary"})
Expand All @@ -90,7 +113,7 @@ def test_array_to_query_parameter_valid_argument(self):
([1.25, 2.50], "FLOAT64"),
([decimal.Decimal("1.25")], "NUMERIC"),
([b"foo", b"bar"], "BYTES"),
([u"foo", u"bar"], "STRING"),
(["foo", "bar"], "STRING"),
([datetime.date(2017, 4, 1), datetime.date(2018, 4, 1)], "DATE"),
([datetime.time(12, 34, 56), datetime.time(10, 20, 30)], "TIME"),
(
Expand Down Expand Up @@ -134,7 +157,7 @@ def test_array_to_query_parameter_empty_argument(self):
_helpers.array_to_query_parameter([])

def test_array_to_query_parameter_unsupported_sequence(self):
unsupported_iterables = [{10, 20, 30}, u"foo", b"bar", bytearray([65, 75, 85])]
unsupported_iterables = [{10, 20, 30}, "foo", b"bar", bytearray([65, 75, 85])]
for iterable in unsupported_iterables:
with self.assertRaises(exceptions.ProgrammingError):
_helpers.array_to_query_parameter(iterable)
Expand All @@ -144,7 +167,7 @@ def test_array_to_query_parameter_sequence_w_invalid_elements(self):
_helpers.array_to_query_parameter([object(), 2, 7])

def test_to_query_parameters_w_dict(self):
parameters = {"somebool": True, "somestring": u"a-string-value"}
parameters = {"somebool": True, "somestring": "a-string-value"}
query_parameters = _helpers.to_query_parameters(parameters)
query_parameter_tuples = []
for param in query_parameters:
Expand All @@ -154,7 +177,7 @@ def test_to_query_parameters_w_dict(self):
sorted(
[
("somebool", "BOOL", True),
("somestring", "STRING", u"a-string-value"),
("somestring", "STRING", "a-string-value"),
]
),
)
Expand All @@ -177,14 +200,14 @@ def test_to_query_parameters_w_dict_dict_param(self):
_helpers.to_query_parameters(parameters)

def test_to_query_parameters_w_list(self):
parameters = [True, u"a-string-value"]
parameters = [True, "a-string-value"]
query_parameters = _helpers.to_query_parameters(parameters)
query_parameter_tuples = []
for param in query_parameters:
query_parameter_tuples.append((param.name, param.type_, param.value))
self.assertSequenceEqual(
sorted(query_parameter_tuples),
sorted([(None, "BOOL", True), (None, "STRING", u"a-string-value")]),
sorted([(None, "BOOL", True), (None, "STRING", "a-string-value")]),
)

def test_to_query_parameters_w_list_array_param(self):
Expand Down