We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
DB-API depends on pyarrow when decimal query parameters are used
It seems we introduced a hard dependency on pyarrow to detect data types of query parameters in the DB-API module. While `pyarrow` is recommended as it's needed to use the BigQuery Storage API, I don't think we want to depend on it for basic functionality.

I believe we added this hard dependency here: https://github.com/googleapis/python-bigquery/pull/527/files#diff-2221cd9b6b85f2ed1f943c474e8a8628e6ca28ec9a96c0aa5873cd6a66826bdeR193

We use `pyarrow` to disambiguate between `NUMERIC` and `BIGNUMERIC`. I'm not seeing a good way to check the precision / scale in the [decimal](https://docs.python.org/3/library/decimal.html) type, so in the case that `pyarrow` is not available, we should guess the type is `NUMERIC`.


#### Stack trace

```
__________________________________________________________________________________ test_dml ___________________________________________________________________________________
test/test_sqlalchemy_bigquery.py:430: in test_dml
    engine.execute(table_dml.insert(ONE_ROW_CONTENTS_DML))
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:2235: in execute
    return connection.execute(statement, *multiparams, **params)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1514: in _handle_dbapi_exception
    util.raise_(exc_info[1], with_traceback=exc_info[2])
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/util/compat.py:182: in raise_
    raise exception
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/default.py:608: in do_execute
    cursor.execute(statement, parameters)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:263: in with_closed_check
    return method(self, *args, **kwargs)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/cursor.py:165: in execute
    query_parameters = _helpers.to_query_parameters(parameters)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:168: in to_query_parameters
    return to_query_parameters_dict(parameters)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:147: in to_query_parameters_dict
    param = scalar_to_query_parameter(value, name=name)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:51: in scalar_to_query_parameter
    parameter_type = bigquery_scalar_type(value)
../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:193: in bigquery_scalar_type
    scalar_object = pyarrow.scalar(value)
E   AttributeError: 'NoneType' object has no attribute 'scalar'
```



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `google/cloud/bigquery/dbapi/_helpers.py`
Content:
```
1 # Copyright 2017 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.
14 
15 
16 from collections import abc as collections_abc
17 import datetime
18 import decimal
19 import functools
20 import numbers
21 
22 try:
23     import pyarrow
24 except ImportError:  # pragma: NO COVER
25     pyarrow = None
26 
27 from google.cloud import bigquery
28 from google.cloud.bigquery import table
29 from google.cloud.bigquery.dbapi import exceptions
30 
31 
32 def scalar_to_query_parameter(value, name=None):
33     """Convert a scalar value into a query parameter.
34 
35     Args:
36         value (Any):
37             A scalar value to convert into a query parameter.
38 
39         name (str):
40             (Optional) Name of the query parameter.
41 
42     Returns:
43         google.cloud.bigquery.ScalarQueryParameter:
44             A query parameter corresponding with the type and value of the plain
45             Python object.
46 
47     Raises:
48         google.cloud.bigquery.dbapi.exceptions.ProgrammingError:
49             if the type cannot be determined.
50     """
51     parameter_type = bigquery_scalar_type(value)
52 
53     if parameter_type is None:
54         raise exceptions.ProgrammingError(
55             "encountered parameter {} with value {} of unexpected type".format(
56                 name, value
57             )
58         )
59     return bigquery.ScalarQueryParameter(name, parameter_type, value)
60 
61 
62 def array_to_query_parameter(value, name=None):
63     """Convert an array-like value into a query parameter.
64 
65     Args:
66         value (Sequence[Any]): The elements of the array (should not be a
67             string-like Sequence).
68         name (Optional[str]): Name of the query parameter.
69 
70     Returns:
71         A query parameter corresponding with the type and value of the plain
72         Python object.
73 
74     Raises:
75         google.cloud.bigquery.dbapi.exceptions.ProgrammingError:
76             if the type of array elements cannot be determined.
77     """
78     if not array_like(value):
79         raise exceptions.ProgrammingError(
80             "The value of parameter {} must be a sequence that is "
81             "not string-like.".format(name)
82         )
83 
84     if not value:
85         raise exceptions.ProgrammingError(
86             "Encountered an empty array-like value of parameter {}, cannot "
87             "determine array elements type.".format(name)
88         )
89 
90     # Assume that all elements are of the same type, and let the backend handle
91     # any type incompatibilities among the array elements
92     array_type = bigquery_scalar_type(value[0])
93     if array_type is None:
94         raise exceptions.ProgrammingError(
95             "Encountered unexpected first array element of parameter {}, "
96             "cannot determine array elements type.".format(name)
97         )
98 
99     return bigquery.ArrayQueryParameter(name, array_type, value)
100 
101 
102 def to_query_parameters_list(parameters):
103     """Converts a sequence of parameter values into query parameters.
104 
105     Args:
106         parameters (Sequence[Any]): Sequence of query parameter values.
107 
108     Returns:
109         List[google.cloud.bigquery.query._AbstractQueryParameter]:
110             A list of query parameters.
111     """
112     result = []
113 
114     for value in parameters:
115         if isinstance(value, collections_abc.Mapping):
116             raise NotImplementedError("STRUCT-like parameter values are not supported.")
117         elif array_like(value):
118             param = array_to_query_parameter(value)
119         else:
120             param = scalar_to_query_parameter(value)
121         result.append(param)
122 
123     return result
124 
125 
126 def to_query_parameters_dict(parameters):
127     """Converts a dictionary of parameter values into query parameters.
128 
129     Args:
130         parameters (Mapping[str, Any]): Dictionary of query parameter values.
131 
132     Returns:
133         List[google.cloud.bigquery.query._AbstractQueryParameter]:
134             A list of named query parameters.
135     """
136     result = []
137 
138     for name, value in parameters.items():
139         if isinstance(value, collections_abc.Mapping):
140             raise NotImplementedError(
141                 "STRUCT-like parameter values are not supported "
142                 "(parameter {}).".format(name)
143             )
144         elif array_like(value):
145             param = array_to_query_parameter(value, name=name)
146         else:
147             param = scalar_to_query_parameter(value, name=name)
148         result.append(param)
149 
150     return result
151 
152 
153 def to_query_parameters(parameters):
154     """Converts DB-API parameter values into query parameters.
155 
156     Args:
157         parameters (Union[Mapping[str, Any], Sequence[Any]]):
158             A dictionary or sequence of query parameter values.
159 
160     Returns:
161         List[google.cloud.bigquery.query._AbstractQueryParameter]:
162             A list of query parameters.
163     """
164     if parameters is None:
165         return []
166 
167     if isinstance(parameters, collections_abc.Mapping):
168         return to_query_parameters_dict(parameters)
169 
170     return to_query_parameters_list(parameters)
171 
172 
173 def bigquery_scalar_type(value):
174     """Return a BigQuery name of the scalar type that matches the given value.
175 
176     If the scalar type name could not be determined (e.g. for non-scalar
177     values), ``None`` is returned.
178 
179     Args:
180         value (Any)
181 
182     Returns:
183         Optional[str]: The BigQuery scalar type name.
184     """
185     if isinstance(value, bool):
186         return "BOOL"
187     elif isinstance(value, numbers.Integral):
188         return "INT64"
189     elif isinstance(value, numbers.Real):
190         return "FLOAT64"
191     elif isinstance(value, decimal.Decimal):
192         # We check for NUMERIC before BIGNUMERIC in order to support pyarrow < 3.0.
193         scalar_object = pyarrow.scalar(value)
194         if isinstance(scalar_object, pyarrow.Decimal128Scalar):
195             return "NUMERIC"
196         else:
197             return "BIGNUMERIC"
198     elif isinstance(value, str):
199         return "STRING"
200     elif isinstance(value, bytes):
201         return "BYTES"
202     elif isinstance(value, datetime.datetime):
203         return "DATETIME" if value.tzinfo is None else "TIMESTAMP"
204     elif isinstance(value, datetime.date):
205         return "DATE"
206     elif isinstance(value, datetime.time):
207         return "TIME"
208 
209     return None
210 
211 
212 def array_like(value):
213     """Determine if the given value is array-like.
214 
215     Examples of array-like values (as interpreted by this function) are
216     sequences such as ``list`` and ``tuple``, but not strings and other
217     iterables such as sets.
218 
219     Args:
220         value (Any)
221 
222     Returns:
223         bool: ``True`` if the value is considered array-like, ``False`` otherwise.
224     """
225     return isinstance(value, collections_abc.Sequence) and not isinstance(
226         value, (str, bytes, bytearray)
227     )
228 
229 
230 def to_bq_table_rows(rows_iterable):
231     """Convert table rows to BigQuery table Row instances.
232 
233     Args:
234         rows_iterable (Iterable[Mapping]):
235             An iterable of row data items to convert to ``Row`` instances.
236 
237     Returns:
238         Iterable[google.cloud.bigquery.table.Row]
239     """
240 
241     def to_table_row(row):
242         # NOTE: We fetch ARROW values, thus we need to convert them to Python
243         # objects with as_py().
244         values = tuple(value.as_py() for value in row.values())
245         keys_to_index = {key: i for i, key in enumerate(row.keys())}
246         return table.Row(values, keys_to_index)
247 
248     return (to_table_row(row_data) for row_data in rows_iterable)
249 
250 
251 def raise_on_closed(
252     exc_msg, exc_class=exceptions.ProgrammingError, closed_attr_name="_closed"
253 ):
254     """Make public instance methods raise an error if the instance is closed."""
255 
256     def _raise_on_closed(method):
257         """Make a non-static method raise an error if its containing instance is closed.
258         """
259 
260         def with_closed_check(self, *args, **kwargs):
261             if getattr(self, closed_attr_name):
262                 raise exc_class(exc_msg)
263             return method(self, *args, **kwargs)
264 
265         functools.update_wrapper(with_closed_check, method)
266         return with_closed_check
267 
268     def decorate_public_methods(klass):
269         """Apply ``_raise_on_closed()`` decorator to public instance methods.
270         """
271         for name in dir(klass):
272             if name.startswith("_"):
273                 continue
274 
275             member = getattr(klass, name)
276             if not callable(member):
277                 continue
278 
279             # We need to check for class/static methods directly in the instance
280             # __dict__, not via the retrieved attribute (`member`), as the
281             # latter is already a callable *produced* by one of these descriptors.
282             if isinstance(klass.__dict__[name], (staticmethod, classmethod)):
283                 continue
284 
285             member = _raise_on_closed(member)
286             setattr(klass, name, member)
287 
288         return klass
289 
290     return decorate_public_methods
291
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/google/cloud/bigquery/dbapi/_helpers.py b/google/cloud/bigquery/dbapi/_helpers.py
--- a/google/cloud/bigquery/dbapi/_helpers.py
+++ b/google/cloud/bigquery/dbapi/_helpers.py
@@ -19,16 +19,15 @@
 import functools
 import numbers
 
-try:
-    import pyarrow
-except ImportError:  # pragma: NO COVER
-    pyarrow = None
-
 from google.cloud import bigquery
 from google.cloud.bigquery import table
 from google.cloud.bigquery.dbapi import exceptions
 
 
+_NUMERIC_SERVER_MIN = decimal.Decimal("-9.9999999999999999999999999999999999999E+28")
+_NUMERIC_SERVER_MAX = decimal.Decimal("9.9999999999999999999999999999999999999E+28")
+
+
 def scalar_to_query_parameter(value, name=None):
     """Convert a scalar value into a query parameter.
 
@@ -189,12 +188,20 @@
     elif isinstance(value, numbers.Real):
         return "FLOAT64"
     elif isinstance(value, decimal.Decimal):
-        # We check for NUMERIC before BIGNUMERIC in order to support pyarrow < 3.0.
-        scalar_object = pyarrow.scalar(value)
-        if isinstance(scalar_object, pyarrow.Decimal128Scalar):
+        vtuple = value.as_tuple()
+        # NUMERIC values have precision of 38 (number of digits) and scale of 9 (number
+        # of fractional digits), and their max absolute value must be strictly smaller
+        # than 1.0E+29.
+        # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#decimal_types
+        if (
+            len(vtuple.digits) <= 38  # max precision: 38
+            and vtuple.exponent >= -9  # max scale: 9
+            and _NUMERIC_SERVER_MIN <= value <= _NUMERIC_SERVER_MAX
+        ):
             return "NUMERIC"
         else:
             return "BIGNUMERIC"
+
     elif isinstance(value, str):
         return "STRING"
     elif isinstance(value, bytes):

{"golden_diff": "diff --git a/google/cloud/bigquery/dbapi/_helpers.py b/google/cloud/bigquery/dbapi/_helpers.py\n--- a/google/cloud/bigquery/dbapi/_helpers.py\n+++ b/google/cloud/bigquery/dbapi/_helpers.py\n@@ -19,16 +19,15 @@\n import functools\n import numbers\n \n-try:\n-    import pyarrow\n-except ImportError:  # pragma: NO COVER\n-    pyarrow = None\n-\n from google.cloud import bigquery\n from google.cloud.bigquery import table\n from google.cloud.bigquery.dbapi import exceptions\n \n \n+_NUMERIC_SERVER_MIN = decimal.Decimal(\"-9.9999999999999999999999999999999999999E+28\")\n+_NUMERIC_SERVER_MAX = decimal.Decimal(\"9.9999999999999999999999999999999999999E+28\")\n+\n+\n def scalar_to_query_parameter(value, name=None):\n     \"\"\"Convert a scalar value into a query parameter.\n \n@@ -189,12 +188,20 @@\n     elif isinstance(value, numbers.Real):\n         return \"FLOAT64\"\n     elif isinstance(value, decimal.Decimal):\n-        # We check for NUMERIC before BIGNUMERIC in order to support pyarrow < 3.0.\n-        scalar_object = pyarrow.scalar(value)\n-        if isinstance(scalar_object, pyarrow.Decimal128Scalar):\n+        vtuple = value.as_tuple()\n+        # NUMERIC values have precision of 38 (number of digits) and scale of 9 (number\n+        # of fractional digits), and their max absolute value must be strictly smaller\n+        # than 1.0E+29.\n+        # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#decimal_types\n+        if (\n+            len(vtuple.digits) <= 38  # max precision: 38\n+            and vtuple.exponent >= -9  # max scale: 9\n+            and _NUMERIC_SERVER_MIN <= value <= _NUMERIC_SERVER_MAX\n+        ):\n             return \"NUMERIC\"\n         else:\n             return \"BIGNUMERIC\"\n+\n     elif isinstance(value, str):\n         return \"STRING\"\n     elif isinstance(value, bytes):\n", "issue": "DB-API depends on pyarrow when decimal query parameters are used\nIt seems we introduced a hard dependency on pyarrow to detect data types of query parameters in the DB-API module. While `pyarrow` is recommended as it's needed to use the BigQuery Storage API, I don't think we want to depend on it for basic functionality.\r\n\r\nI believe we added this hard dependency here: https://github.com/googleapis/python-bigquery/pull/527/files#diff-2221cd9b6b85f2ed1f943c474e8a8628e6ca28ec9a96c0aa5873cd6a66826bdeR193\r\n\r\nWe use `pyarrow` to disambiguate between `NUMERIC` and `BIGNUMERIC`. I'm not seeing a good way to check the precision / scale in the [decimal](https://docs.python.org/3/library/decimal.html) type, so in the case that `pyarrow` is not available, we should guess the type is `NUMERIC`.\r\n\r\n\r\n#### Stack trace\r\n\r\n```\r\n__________________________________________________________________________________ test_dml ___________________________________________________________________________________\r\ntest/test_sqlalchemy_bigquery.py:430: in test_dml\r\n    engine.execute(table_dml.insert(ONE_ROW_CONTENTS_DML))\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:2235: in execute\r\n    return connection.execute(statement, *multiparams, **params)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1011: in execute\r\n    return meth(self, multiparams, params)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/sql/elements.py:298: in _execute_on_connection\r\n    return connection._execute_clauseelement(self, multiparams, params)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1124: in _execute_clauseelement\r\n    ret = self._execute_context(\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1316: in _execute_context\r\n    self._handle_dbapi_exception(\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1514: in _handle_dbapi_exception\r\n    util.raise_(exc_info[1], with_traceback=exc_info[2])\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/util/compat.py:182: in raise_\r\n    raise exception\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/base.py:1276: in _execute_context\r\n    self.dialect.do_execute(\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/sqlalchemy/engine/default.py:608: in do_execute\r\n    cursor.execute(statement, parameters)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:263: in with_closed_check\r\n    return method(self, *args, **kwargs)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/cursor.py:165: in execute\r\n    query_parameters = _helpers.to_query_parameters(parameters)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:168: in to_query_parameters\r\n    return to_query_parameters_dict(parameters)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:147: in to_query_parameters_dict\r\n    param = scalar_to_query_parameter(value, name=name)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:51: in scalar_to_query_parameter\r\n    parameter_type = bigquery_scalar_type(value)\r\n../../miniconda3/envs/pybigquery/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py:193: in bigquery_scalar_type\r\n    scalar_object = pyarrow.scalar(value)\r\nE   AttributeError: 'NoneType' object has no attribute 'scalar'\r\n```\r\n\r\n\n", "before_files": [{"content": "# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n#  Unless required by applicable law or agreed to in writing, software\n#  distributed under the License is distributed on an \"AS IS\" BASIS,\n#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#  See the License for the specific language governing permissions and\n#  limitations under the License.\n\n\nfrom collections import abc as collections_abc\nimport datetime\nimport decimal\nimport functools\nimport numbers\n\ntry:\n    import pyarrow\nexcept ImportError:  # pragma: NO COVER\n    pyarrow = None\n\nfrom google.cloud import bigquery\nfrom google.cloud.bigquery import table\nfrom google.cloud.bigquery.dbapi import exceptions\n\n\ndef scalar_to_query_parameter(value, name=None):\n    \"\"\"Convert a scalar value into a query parameter.\n\n    Args:\n        value (Any):\n            A scalar value to convert into a query parameter.\n\n        name (str):\n            (Optional) Name of the query parameter.\n\n    Returns:\n        google.cloud.bigquery.ScalarQueryParameter:\n            A query parameter corresponding with the type and value of the plain\n            Python object.\n\n    Raises:\n        google.cloud.bigquery.dbapi.exceptions.ProgrammingError:\n            if the type cannot be determined.\n    \"\"\"\n    parameter_type = bigquery_scalar_type(value)\n\n    if parameter_type is None:\n        raise exceptions.ProgrammingError(\n            \"encountered parameter {} with value {} of unexpected type\".format(\n                name, value\n            )\n        )\n    return bigquery.ScalarQueryParameter(name, parameter_type, value)\n\n\ndef array_to_query_parameter(value, name=None):\n    \"\"\"Convert an array-like value into a query parameter.\n\n    Args:\n        value (Sequence[Any]): The elements of the array (should not be a\n            string-like Sequence).\n        name (Optional[str]): Name of the query parameter.\n\n    Returns:\n        A query parameter corresponding with the type and value of the plain\n        Python object.\n\n    Raises:\n        google.cloud.bigquery.dbapi.exceptions.ProgrammingError:\n            if the type of array elements cannot be determined.\n    \"\"\"\n    if not array_like(value):\n        raise exceptions.ProgrammingError(\n            \"The value of parameter {} must be a sequence that is \"\n            \"not string-like.\".format(name)\n        )\n\n    if not value:\n        raise exceptions.ProgrammingError(\n            \"Encountered an empty array-like value of parameter {}, cannot \"\n            \"determine array elements type.\".format(name)\n        )\n\n    # Assume that all elements are of the same type, and let the backend handle\n    # any type incompatibilities among the array elements\n    array_type = bigquery_scalar_type(value[0])\n    if array_type is None:\n        raise exceptions.ProgrammingError(\n            \"Encountered unexpected first array element of parameter {}, \"\n            \"cannot determine array elements type.\".format(name)\n        )\n\n    return bigquery.ArrayQueryParameter(name, array_type, value)\n\n\ndef to_query_parameters_list(parameters):\n    \"\"\"Converts a sequence of parameter values into query parameters.\n\n    Args:\n        parameters (Sequence[Any]): Sequence of query parameter values.\n\n    Returns:\n        List[google.cloud.bigquery.query._AbstractQueryParameter]:\n            A list of query parameters.\n    \"\"\"\n    result = []\n\n    for value in parameters:\n        if isinstance(value, collections_abc.Mapping):\n            raise NotImplementedError(\"STRUCT-like parameter values are not supported.\")\n        elif array_like(value):\n            param = array_to_query_parameter(value)\n        else:\n            param = scalar_to_query_parameter(value)\n        result.append(param)\n\n    return result\n\n\ndef to_query_parameters_dict(parameters):\n    \"\"\"Converts a dictionary of parameter values into query parameters.\n\n    Args:\n        parameters (Mapping[str, Any]): Dictionary of query parameter values.\n\n    Returns:\n        List[google.cloud.bigquery.query._AbstractQueryParameter]:\n            A list of named query parameters.\n    \"\"\"\n    result = []\n\n    for name, value in parameters.items():\n        if isinstance(value, collections_abc.Mapping):\n            raise NotImplementedError(\n                \"STRUCT-like parameter values are not supported \"\n                \"(parameter {}).\".format(name)\n            )\n        elif array_like(value):\n            param = array_to_query_parameter(value, name=name)\n        else:\n            param = scalar_to_query_parameter(value, name=name)\n        result.append(param)\n\n    return result\n\n\ndef to_query_parameters(parameters):\n    \"\"\"Converts DB-API parameter values into query parameters.\n\n    Args:\n        parameters (Union[Mapping[str, Any], Sequence[Any]]):\n            A dictionary or sequence of query parameter values.\n\n    Returns:\n        List[google.cloud.bigquery.query._AbstractQueryParameter]:\n            A list of query parameters.\n    \"\"\"\n    if parameters is None:\n        return []\n\n    if isinstance(parameters, collections_abc.Mapping):\n        return to_query_parameters_dict(parameters)\n\n    return to_query_parameters_list(parameters)\n\n\ndef bigquery_scalar_type(value):\n    \"\"\"Return a BigQuery name of the scalar type that matches the given value.\n\n    If the scalar type name could not be determined (e.g. for non-scalar\n    values), ``None`` is returned.\n\n    Args:\n        value (Any)\n\n    Returns:\n        Optional[str]: The BigQuery scalar type name.\n    \"\"\"\n    if isinstance(value, bool):\n        return \"BOOL\"\n    elif isinstance(value, numbers.Integral):\n        return \"INT64\"\n    elif isinstance(value, numbers.Real):\n        return \"FLOAT64\"\n    elif isinstance(value, decimal.Decimal):\n        # We check for NUMERIC before BIGNUMERIC in order to support pyarrow < 3.0.\n        scalar_object = pyarrow.scalar(value)\n        if isinstance(scalar_object, pyarrow.Decimal128Scalar):\n            return \"NUMERIC\"\n        else:\n            return \"BIGNUMERIC\"\n    elif isinstance(value, str):\n        return \"STRING\"\n    elif isinstance(value, bytes):\n        return \"BYTES\"\n    elif isinstance(value, datetime.datetime):\n        return \"DATETIME\" if value.tzinfo is None else \"TIMESTAMP\"\n    elif isinstance(value, datetime.date):\n        return \"DATE\"\n    elif isinstance(value, datetime.time):\n        return \"TIME\"\n\n    return None\n\n\ndef array_like(value):\n    \"\"\"Determine if the given value is array-like.\n\n    Examples of array-like values (as interpreted by this function) are\n    sequences such as ``list`` and ``tuple``, but not strings and other\n    iterables such as sets.\n\n    Args:\n        value (Any)\n\n    Returns:\n        bool: ``True`` if the value is considered array-like, ``False`` otherwise.\n    \"\"\"\n    return isinstance(value, collections_abc.Sequence) and not isinstance(\n        value, (str, bytes, bytearray)\n    )\n\n\ndef to_bq_table_rows(rows_iterable):\n    \"\"\"Convert table rows to BigQuery table Row instances.\n\n    Args:\n        rows_iterable (Iterable[Mapping]):\n            An iterable of row data items to convert to ``Row`` instances.\n\n    Returns:\n        Iterable[google.cloud.bigquery.table.Row]\n    \"\"\"\n\n    def to_table_row(row):\n        # NOTE: We fetch ARROW values, thus we need to convert them to Python\n        # objects with as_py().\n        values = tuple(value.as_py() for value in row.values())\n        keys_to_index = {key: i for i, key in enumerate(row.keys())}\n        return table.Row(values, keys_to_index)\n\n    return (to_table_row(row_data) for row_data in rows_iterable)\n\n\ndef raise_on_closed(\n    exc_msg, exc_class=exceptions.ProgrammingError, closed_attr_name=\"_closed\"\n):\n    \"\"\"Make public instance methods raise an error if the instance is closed.\"\"\"\n\n    def _raise_on_closed(method):\n        \"\"\"Make a non-static method raise an error if its containing instance is closed.\n        \"\"\"\n\n        def with_closed_check(self, *args, **kwargs):\n            if getattr(self, closed_attr_name):\n                raise exc_class(exc_msg)\n            return method(self, *args, **kwargs)\n\n        functools.update_wrapper(with_closed_check, method)\n        return with_closed_check\n\n    def decorate_public_methods(klass):\n        \"\"\"Apply ``_raise_on_closed()`` decorator to public instance methods.\n        \"\"\"\n        for name in dir(klass):\n            if name.startswith(\"_\"):\n                continue\n\n            member = getattr(klass, name)\n            if not callable(member):\n                continue\n\n            # We need to check for class/static methods directly in the instance\n            # __dict__, not via the retrieved attribute (`member`), as the\n            # latter is already a callable *produced* by one of these descriptors.\n            if isinstance(klass.__dict__[name], (staticmethod, classmethod)):\n                continue\n\n            member = _raise_on_closed(member)\n            setattr(klass, name, member)\n\n        return klass\n\n    return decorate_public_methods\n", "path": "google/cloud/bigquery/dbapi/_helpers.py"}], "after_files": [{"content": "# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n#  Unless required by applicable law or agreed to in writing, software\n#  distributed under the License is distributed on an \"AS IS\" BASIS,\n#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#  See the License for the specific language governing permissions and\n#  limitations under the License.\n\n\nfrom collections import abc as collections_abc\nimport datetime\nimport decimal\nimport functools\nimport numbers\n\nfrom google.cloud import bigquery\nfrom google.cloud.bigquery import table\nfrom google.cloud.bigquery.dbapi import exceptions\n\n\n_NUMERIC_SERVER_MIN = decimal.Decimal(\"-9.9999999999999999999999999999999999999E+28\")\n_NUMERIC_SERVER_MAX = decimal.Decimal(\"9.9999999999999999999999999999999999999E+28\")\n\n\ndef scalar_to_query_parameter(value, name=None):\n    \"\"\"Convert a scalar value into a query parameter.\n\n    Args:\n        value (Any):\n            A scalar value to convert into a query parameter.\n\n        name (str):\n            (Optional) Name of the query parameter.\n\n    Returns:\n        google.cloud.bigquery.ScalarQueryParameter:\n            A query parameter corresponding with the type and value of the plain\n            Python object.\n\n    Raises:\n        google.cloud.bigquery.dbapi.exceptions.ProgrammingError:\n            if the type cannot be determined.\n    \"\"\"\n    parameter_type = bigquery_scalar_type(value)\n\n    if parameter_type is None:\n        raise exceptions.ProgrammingError(\n            \"encountered parameter {} with value {} of unexpected type\".format(\n                name, value\n            )\n        )\n    return bigquery.ScalarQueryParameter(name, parameter_type, value)\n\n\ndef array_to_query_parameter(value, name=None):\n    \"\"\"Convert an array-like value into a query parameter.\n\n    Args:\n        value (Sequence[Any]): The elements of the array (should not be a\n            string-like Sequence).\n        name (Optional[str]): Name of the query parameter.\n\n    Returns:\n        A query parameter corresponding with the type and value of the plain\n        Python object.\n\n    Raises:\n        google.cloud.bigquery.dbapi.exceptions.ProgrammingError:\n            if the type of array elements cannot be determined.\n    \"\"\"\n    if not array_like(value):\n        raise exceptions.ProgrammingError(\n            \"The value of parameter {} must be a sequence that is \"\n            \"not string-like.\".format(name)\n        )\n\n    if not value:\n        raise exceptions.ProgrammingError(\n            \"Encountered an empty array-like value of parameter {}, cannot \"\n            \"determine array elements type.\".format(name)\n        )\n\n    # Assume that all elements are of the same type, and let the backend handle\n    # any type incompatibilities among the array elements\n    array_type = bigquery_scalar_type(value[0])\n    if array_type is None:\n        raise exceptions.ProgrammingError(\n            \"Encountered unexpected first array element of parameter {}, \"\n            \"cannot determine array elements type.\".format(name)\n        )\n\n    return bigquery.ArrayQueryParameter(name, array_type, value)\n\n\ndef to_query_parameters_list(parameters):\n    \"\"\"Converts a sequence of parameter values into query parameters.\n\n    Args:\n        parameters (Sequence[Any]): Sequence of query parameter values.\n\n    Returns:\n        List[google.cloud.bigquery.query._AbstractQueryParameter]:\n            A list of query parameters.\n    \"\"\"\n    result = []\n\n    for value in parameters:\n        if isinstance(value, collections_abc.Mapping):\n            raise NotImplementedError(\"STRUCT-like parameter values are not supported.\")\n        elif array_like(value):\n            param = array_to_query_parameter(value)\n        else:\n            param = scalar_to_query_parameter(value)\n        result.append(param)\n\n    return result\n\n\ndef to_query_parameters_dict(parameters):\n    \"\"\"Converts a dictionary of parameter values into query parameters.\n\n    Args:\n        parameters (Mapping[str, Any]): Dictionary of query parameter values.\n\n    Returns:\n        List[google.cloud.bigquery.query._AbstractQueryParameter]:\n            A list of named query parameters.\n    \"\"\"\n    result = []\n\n    for name, value in parameters.items():\n        if isinstance(value, collections_abc.Mapping):\n            raise NotImplementedError(\n                \"STRUCT-like parameter values are not supported \"\n                \"(parameter {}).\".format(name)\n            )\n        elif array_like(value):\n            param = array_to_query_parameter(value, name=name)\n        else:\n            param = scalar_to_query_parameter(value, name=name)\n        result.append(param)\n\n    return result\n\n\ndef to_query_parameters(parameters):\n    \"\"\"Converts DB-API parameter values into query parameters.\n\n    Args:\n        parameters (Union[Mapping[str, Any], Sequence[Any]]):\n            A dictionary or sequence of query parameter values.\n\n    Returns:\n        List[google.cloud.bigquery.query._AbstractQueryParameter]:\n            A list of query parameters.\n    \"\"\"\n    if parameters is None:\n        return []\n\n    if isinstance(parameters, collections_abc.Mapping):\n        return to_query_parameters_dict(parameters)\n\n    return to_query_parameters_list(parameters)\n\n\ndef bigquery_scalar_type(value):\n    \"\"\"Return a BigQuery name of the scalar type that matches the given value.\n\n    If the scalar type name could not be determined (e.g. for non-scalar\n    values), ``None`` is returned.\n\n    Args:\n        value (Any)\n\n    Returns:\n        Optional[str]: The BigQuery scalar type name.\n    \"\"\"\n    if isinstance(value, bool):\n        return \"BOOL\"\n    elif isinstance(value, numbers.Integral):\n        return \"INT64\"\n    elif isinstance(value, numbers.Real):\n        return \"FLOAT64\"\n    elif isinstance(value, decimal.Decimal):\n        vtuple = value.as_tuple()\n        # NUMERIC values have precision of 38 (number of digits) and scale of 9 (number\n        # of fractional digits), and their max absolute value must be strictly smaller\n        # than 1.0E+29.\n        # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#decimal_types\n        if (\n            len(vtuple.digits) <= 38  # max precision: 38\n            and vtuple.exponent >= -9  # max scale: 9\n            and _NUMERIC_SERVER_MIN <= value <= _NUMERIC_SERVER_MAX\n        ):\n            return \"NUMERIC\"\n        else:\n            return \"BIGNUMERIC\"\n\n    elif isinstance(value, str):\n        return \"STRING\"\n    elif isinstance(value, bytes):\n        return \"BYTES\"\n    elif isinstance(value, datetime.datetime):\n        return \"DATETIME\" if value.tzinfo is None else \"TIMESTAMP\"\n    elif isinstance(value, datetime.date):\n        return \"DATE\"\n    elif isinstance(value, datetime.time):\n        return \"TIME\"\n\n    return None\n\n\ndef array_like(value):\n    \"\"\"Determine if the given value is array-like.\n\n    Examples of array-like values (as interpreted by this function) are\n    sequences such as ``list`` and ``tuple``, but not strings and other\n    iterables such as sets.\n\n    Args:\n        value (Any)\n\n    Returns:\n        bool: ``True`` if the value is considered array-like, ``False`` otherwise.\n    \"\"\"\n    return isinstance(value, collections_abc.Sequence) and not isinstance(\n        value, (str, bytes, bytearray)\n    )\n\n\ndef to_bq_table_rows(rows_iterable):\n    \"\"\"Convert table rows to BigQuery table Row instances.\n\n    Args:\n        rows_iterable (Iterable[Mapping]):\n            An iterable of row data items to convert to ``Row`` instances.\n\n    Returns:\n        Iterable[google.cloud.bigquery.table.Row]\n    \"\"\"\n\n    def to_table_row(row):\n        # NOTE: We fetch ARROW values, thus we need to convert them to Python\n        # objects with as_py().\n        values = tuple(value.as_py() for value in row.values())\n        keys_to_index = {key: i for i, key in enumerate(row.keys())}\n        return table.Row(values, keys_to_index)\n\n    return (to_table_row(row_data) for row_data in rows_iterable)\n\n\ndef raise_on_closed(\n    exc_msg, exc_class=exceptions.ProgrammingError, closed_attr_name=\"_closed\"\n):\n    \"\"\"Make public instance methods raise an error if the instance is closed.\"\"\"\n\n    def _raise_on_closed(method):\n        \"\"\"Make a non-static method raise an error if its containing instance is closed.\n        \"\"\"\n\n        def with_closed_check(self, *args, **kwargs):\n            if getattr(self, closed_attr_name):\n                raise exc_class(exc_msg)\n            return method(self, *args, **kwargs)\n\n        functools.update_wrapper(with_closed_check, method)\n        return with_closed_check\n\n    def decorate_public_methods(klass):\n        \"\"\"Apply ``_raise_on_closed()`` decorator to public instance methods.\n        \"\"\"\n        for name in dir(klass):\n            if name.startswith(\"_\"):\n                continue\n\n            member = getattr(klass, name)\n            if not callable(member):\n                continue\n\n            # We need to check for class/static methods directly in the instance\n            # __dict__, not via the retrieved attribute (`member`), as the\n            # latter is already a callable *produced* by one of these descriptors.\n            if isinstance(klass.__dict__[name], (staticmethod, classmethod)):\n                continue\n\n            member = _raise_on_closed(member)\n            setattr(klass, name, member)\n\n        return klass\n\n    return decorate_public_methods\n", "path": "google/cloud/bigquery/dbapi/_helpers.py"}]}