-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation of DB-API for BigQuery. (#2921)
The `google.cloud.bigquery.dbapi` package covers all of the required implementation details in the PEP-249 DB-API specification.
- Loading branch information
Showing
11 changed files
with
1,432 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Copyright 2017 Google Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""Google BigQuery implementation of the Database API Specification v2.0. | ||
This module implements the `Python Database API Specification v2.0 (DB-API)`_ | ||
for Google BigQuery. | ||
.. _Python Database API Specification v2.0 (DB-API): | ||
https://www.python.org/dev/peps/pep-0249/ | ||
.. warning:: | ||
The ``dbapi`` module is **alpha**. The implementation is not complete. It | ||
might be changed in backward-incompatible ways and is not subject to any SLA | ||
or deprecation policy. | ||
""" | ||
|
||
from google.cloud.bigquery.dbapi.connection import connect | ||
from google.cloud.bigquery.dbapi.connection import Connection | ||
from google.cloud.bigquery.dbapi.cursor import Cursor | ||
from google.cloud.bigquery.dbapi.exceptions import Warning | ||
from google.cloud.bigquery.dbapi.exceptions import Error | ||
from google.cloud.bigquery.dbapi.exceptions import InterfaceError | ||
from google.cloud.bigquery.dbapi.exceptions import DatabaseError | ||
from google.cloud.bigquery.dbapi.exceptions import DataError | ||
from google.cloud.bigquery.dbapi.exceptions import OperationalError | ||
from google.cloud.bigquery.dbapi.exceptions import IntegrityError | ||
from google.cloud.bigquery.dbapi.exceptions import InternalError | ||
from google.cloud.bigquery.dbapi.exceptions import ProgrammingError | ||
from google.cloud.bigquery.dbapi.exceptions import NotSupportedError | ||
from google.cloud.bigquery.dbapi.types import Binary | ||
from google.cloud.bigquery.dbapi.types import Date | ||
from google.cloud.bigquery.dbapi.types import DateFromTicks | ||
from google.cloud.bigquery.dbapi.types import Time | ||
from google.cloud.bigquery.dbapi.types import TimeFromTicks | ||
from google.cloud.bigquery.dbapi.types import Timestamp | ||
from google.cloud.bigquery.dbapi.types import TimestampFromTicks | ||
from google.cloud.bigquery.dbapi.types import BINARY | ||
from google.cloud.bigquery.dbapi.types import DATETIME | ||
from google.cloud.bigquery.dbapi.types import NUMBER | ||
from google.cloud.bigquery.dbapi.types import ROWID | ||
from google.cloud.bigquery.dbapi.types import STRING | ||
|
||
|
||
apilevel = '2.0' | ||
|
||
# Threads may share the module, but not connections. | ||
threadsafety = 1 | ||
|
||
paramstyle = 'pyformat' | ||
|
||
__all__ = [ | ||
'apilevel', 'threadsafety', 'paramstyle', 'connect', 'Connection', | ||
'Cursor', 'Warning', 'Error', 'InterfaceError', 'DatabaseError', | ||
'DataError', 'OperationalError', 'IntegrityError', 'InternalError', | ||
'ProgrammingError', 'NotSupportedError', 'Binary', 'Date', 'DateFromTicks', | ||
'Time', 'TimeFromTicks', 'Timestamp', 'TimestampFromTicks', 'BINARY', | ||
'DATETIME', 'NUMBER', 'ROWID', 'STRING', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# Copyright 2017 Google Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import collections | ||
import datetime | ||
import numbers | ||
import time | ||
|
||
import six | ||
|
||
from google.cloud import bigquery | ||
from google.cloud.bigquery.dbapi import exceptions | ||
|
||
|
||
def wait_for_job(job): | ||
"""Waits for a job to complete by polling until the state is `DONE`. | ||
Sleeps 1 second between calls to the BigQuery API. | ||
:type job: :class:`~google.cloud.bigquery.job._AsyncJob` | ||
:param job: Wait for this job to finish. | ||
:raises: :class:`~google.cloud.bigquery.dbapi.exceptions.DatabaseError` | ||
if the job fails. | ||
""" | ||
while True: | ||
job.reload() | ||
if job.state == 'DONE': | ||
if job.error_result: | ||
raise exceptions.DatabaseError(job.errors) | ||
return | ||
time.sleep(1) | ||
|
||
|
||
def scalar_to_query_parameter(value, name=None): | ||
"""Convert a scalar value into a query parameter. | ||
:type value: any | ||
:param value: A scalar value to convert into a query parameter. | ||
:type name: str | ||
:param name: (Optional) Name of the query parameter. | ||
:rtype: :class:`~google.cloud.bigquery.ScalarQueryParameter` | ||
:returns: | ||
A query parameter corresponding with the type and value of the plain | ||
Python object. | ||
:raises: :class:`~google.cloud.bigquery.dbapi.exceptions.ProgrammingError` | ||
if the type cannot be determined. | ||
""" | ||
parameter_type = None | ||
|
||
if isinstance(value, bool): | ||
parameter_type = 'BOOL' | ||
elif isinstance(value, numbers.Integral): | ||
parameter_type = 'INT64' | ||
elif isinstance(value, numbers.Real): | ||
parameter_type = 'FLOAT64' | ||
elif isinstance(value, six.text_type): | ||
parameter_type = 'STRING' | ||
elif isinstance(value, six.binary_type): | ||
parameter_type = 'BYTES' | ||
elif isinstance(value, datetime.datetime): | ||
parameter_type = 'DATETIME' if value.tzinfo is None else 'TIMESTAMP' | ||
elif isinstance(value, datetime.date): | ||
parameter_type = 'DATE' | ||
elif isinstance(value, datetime.time): | ||
parameter_type = 'TIME' | ||
else: | ||
raise exceptions.ProgrammingError( | ||
'encountered parameter {} with value {} of unexpected type'.format( | ||
name, value)) | ||
return bigquery.ScalarQueryParameter(name, parameter_type, value) | ||
|
||
|
||
def to_query_parameters_list(parameters): | ||
"""Converts a sequence of parameter values into query parameters. | ||
:type parameters: Sequence[Any] | ||
:param parameters: Sequence of query parameter values. | ||
:rtype: List[google.cloud.bigquery._helpers.AbstractQueryParameter] | ||
:returns: A list of query parameters. | ||
""" | ||
return [scalar_to_query_parameter(value) for value in parameters] | ||
|
||
|
||
def to_query_parameters_dict(parameters): | ||
"""Converts a dictionary of parameter values into query parameters. | ||
:type parameters: Mapping[str, Any] | ||
:param parameters: Dictionary of query parameter values. | ||
:rtype: List[google.cloud.bigquery._helpers.AbstractQueryParameter] | ||
:returns: A list of named query parameters. | ||
""" | ||
return [ | ||
scalar_to_query_parameter(value, name=name) | ||
for name, value | ||
in six.iteritems(parameters)] | ||
|
||
|
||
def to_query_parameters(parameters): | ||
"""Converts DB-API parameter values into query parameters. | ||
:type parameters: Mapping[str, Any] or Sequence[Any] | ||
:param parameters: A dictionary or sequence of query parameter values. | ||
:rtype: List[google.cloud.bigquery._helpers.AbstractQueryParameter] | ||
:returns: A list of query parameters. | ||
""" | ||
if parameters is None: | ||
return [] | ||
|
||
if isinstance(parameters, collections.Mapping): | ||
return to_query_parameters_dict(parameters) | ||
|
||
return to_query_parameters_list(parameters) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Copyright 2017 Google Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""Connection for the Google BigQuery DB-API.""" | ||
|
||
from google.cloud import bigquery | ||
from google.cloud.bigquery.dbapi import cursor | ||
|
||
|
||
class Connection(object): | ||
"""DB-API Connection to Google BigQuery. | ||
:type client: :class:`~google.cloud.bigquery.Client` | ||
:param client: A client used to connect to BigQuery. | ||
""" | ||
def __init__(self, client): | ||
self._client = client | ||
|
||
def close(self): | ||
"""No-op.""" | ||
|
||
def commit(self): | ||
"""No-op.""" | ||
|
||
def cursor(self): | ||
"""Return a new cursor object. | ||
:rtype: :class:`~google.cloud.bigquery.dbapi.Cursor` | ||
:returns: A DB-API cursor that uses this connection. | ||
""" | ||
return cursor.Cursor(self) | ||
|
||
|
||
def connect(client=None): | ||
"""Construct a DB-API connection to Google BigQuery. | ||
:type client: :class:`~google.cloud.bigquery.Client` | ||
:param client: | ||
(Optional) A client used to connect to BigQuery. If not passed, a | ||
client is created using default options inferred from the environment. | ||
:rtype: :class:`~google.cloud.bigquery.dbapi.Connection` | ||
:returns: A new DB-API connection to BigQuery. | ||
""" | ||
if client is None: | ||
client = bigquery.Client() | ||
return Connection(client) |
Oops, something went wrong.