Skip to content

Commit

Permalink
Partial implementation of DB-API for BigQuery.
Browse files Browse the repository at this point in the history
I believe this commit now covers all of the required implementation
details in the PEP-249 DB-API specification.
  • Loading branch information
tswast committed Jun 20, 2017
1 parent 6618165 commit 91de44a
Show file tree
Hide file tree
Showing 10 changed files with 1,183 additions and 2 deletions.
61 changes: 61 additions & 0 deletions bigquery/google/cloud/bigquery/dbapi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright 2016 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 # noqa
from google.cloud.bigquery.dbapi.connection import Connection # noqa
from google.cloud.bigquery.dbapi.cursor import Cursor # noqa
from google.cloud.bigquery.dbapi.exceptions import Warning # noqa
from google.cloud.bigquery.dbapi.exceptions import Error # noqa
from google.cloud.bigquery.dbapi.exceptions import InterfaceError # noqa
from google.cloud.bigquery.dbapi.exceptions import DatabaseError # noqa
from google.cloud.bigquery.dbapi.exceptions import DataError # noqa
from google.cloud.bigquery.dbapi.exceptions import OperationalError # noqa
from google.cloud.bigquery.dbapi.exceptions import IntegrityError # noqa
from google.cloud.bigquery.dbapi.exceptions import InternalError # noqa
from google.cloud.bigquery.dbapi.exceptions import ProgrammingError # noqa
from google.cloud.bigquery.dbapi.exceptions import NotSupportedError # noqa
from google.cloud.bigquery.dbapi.types import Binary # noqa
from google.cloud.bigquery.dbapi.types import Date # noqa
from google.cloud.bigquery.dbapi.types import DateFromTicks # noqa
from google.cloud.bigquery.dbapi.types import Time # noqa
from google.cloud.bigquery.dbapi.types import TimeFromTicks # noqa
from google.cloud.bigquery.dbapi.types import Timestamp # noqa
from google.cloud.bigquery.dbapi.types import TimestampFromTicks # noqa
from google.cloud.bigquery.dbapi.types import BINARY # noqa
from google.cloud.bigquery.dbapi.types import DATETIME # noqa
from google.cloud.bigquery.dbapi.types import NUMBER # noqa
from google.cloud.bigquery.dbapi.types import ROWID # noqa
from google.cloud.bigquery.dbapi.types import STRING # noqa


apilevel = "2.0"

# Threads may share the module, but not connections.
threadsafety = 1

paramstyle = "pyformat"
138 changes: 138 additions & 0 deletions bigquery/google/cloud/bigquery/dbapi/_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Copyright 2016 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 six
import time

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`.
Raises a DatabaseError if the job fails.
:type: :class:`~google.cloud.bigquery.job._AsyncJob`
:param job: Wait for this job to finish.
"""
while True:
job.reload()
if job.state == 'DONE':
if job.error_result:
# TODO: raise a more specific exception, based on the error.
# See: https://cloud.google.com/bigquery/troubleshooting-errors
raise exceptions.DatabaseError(job.errors)
return
time.sleep(1)


def scalar_to_query_parameter(name=None, value=None):
"""Convert a scalar value into a query parameter.
Note: the bytes type cannot be distinguished from a string in Python 2.
Raises a :class:`~ google.cloud.bigquery.dbapi.exceptions.ProgrammingError`
if the type cannot be determined.
For more information about BigQuery data types, see:
https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types
:type: str
:param name: Optional name of the query parameter.
:type: any
:param value: A scalar value to convert into a query parameter.
:rtype: :class:`~google.cloud.bigquery.ScalarQueryParameter`
"""
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.string_types):
parameter_type = 'STRING'
elif isinstance(value, bytes):
parameter_type = 'BYTES'
elif isinstance(value, datetime.datetime):
parameter_type = 'TIMESTAMP' if value.tzinfo else 'DATETIME'
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 list of parameter values into query parameters.
:type: list
:param parameters: List of query parameter values.
:rtype:
list of :class:`~google.cloud.bigquery._helpers.AbstractQueryParameter`
"""
query_parameters = []

for value in parameters:
query_parameters.append(scalar_to_query_parameter(value=value))

return query_parameters


def to_query_parameters_dict(parameters):
"""Converts a dictionary of parameter values into query parameters.
:type: dict
:param parameters: Dictionary of query parameter values.
:rtype:
list of :class:`~google.cloud.bigquery._helpers.AbstractQueryParameter`
"""
query_parameters = []

for name in parameters:
value = parameters[name]
query_parameters.append(scalar_to_query_parameter(name, value))

return query_parameters


def to_query_parameters(parameters):
"""Converts DB-API parameter values into query parameters.
:type: dict or list
:param parameters: Optional dictionary or list of query parameter values.
:rtype:
list of :class:`~google.cloud.bigquery._helpers.AbstractQueryParameter`
"""
if parameters is None:
return []

if isinstance(parameters, collections.Mapping):
return to_query_parameters_dict(parameters)

return to_query_parameters_list(parameters)
56 changes: 56 additions & 0 deletions bigquery/google/cloud/bigquery/dbapi/connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright 2016 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: :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`
"""
return cursor.Cursor(self)


def connect(client=None):
"""Construct a DB-API connection to Google BigQuery.
:type: :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`
"""
if client is None:
client = bigquery.Client()
return Connection(client)
Loading

0 comments on commit 91de44a

Please sign in to comment.