| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- # Licensed to the Apache Software Foundation (ASF) under one or more
- # contributor license agreements. See the NOTICE file distributed with
- # this work for additional information regarding copyright ownership.
- # The ASF licenses this file to You 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 logging
- import uuid
- import weakref
- from phoenixdb import errors
- from phoenixdb.cursor import Cursor
- from phoenixdb.errors import ProgrammingError
- from phoenixdb.meta import Meta
- __all__ = ['Connection']
- logger = logging.getLogger(__name__)
- AVATICA_PROPERTIES = ('autoCommit', 'autocommit', 'readOnly', 'readonly', 'transactionIsolation',
- 'catalog', 'schema')
- class Connection(object):
- """Database connection.
- You should not construct this object manually, use :func:`~phoenixdb.connect` instead.
- """
- cursor_factory = None
- """
- The default cursor factory used by :meth:`cursor` if the parameter is not specified.
- """
- def __init__(self, client, cursor_factory=None, **kwargs):
- self._client = client
- self._closed = False
- if cursor_factory is not None:
- self.cursor_factory = cursor_factory
- else:
- self.cursor_factory = Cursor
- self._cursors = []
- self._phoenix_props, avatica_props_init = Connection._map_conn_props(kwargs)
- self.open()
- # TODO we could probably optimize it away if the defaults are not changed
- self.set_session(**avatica_props_init)
- def __del__(self):
- if not self._closed:
- self.close()
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- if not self._closed:
- self.close()
- @staticmethod
- def _default_avatica_props():
- return {'autoCommit': False,
- 'readOnly': False,
- 'transactionIsolation': 0,
- 'catalog': '',
- 'schema': ''}
- @staticmethod
- def _map_conn_props(conn_props):
- """Sorts and prepocesses args that should be passed to Phoenix and Avatica"""
- avatica_props = dict([(k, conn_props[k]) for k in conn_props.keys() if k in AVATICA_PROPERTIES])
- phoenix_props = dict([(k, conn_props[k]) for k in conn_props.keys() if k not in AVATICA_PROPERTIES])
- avatica_props = Connection._map_legacy_avatica_props(avatica_props)
- return (phoenix_props, avatica_props)
- @staticmethod
- def _map_legacy_avatica_props(props):
- if 'autocommit' in props:
- props['autoCommit'] = bool(props.pop('autocommit'))
- if 'readonly' in props:
- props['readOnly'] = bool(props.pop('readonly'))
- return props
- def open(self):
- """Opens the connection."""
- self._id = str(uuid.uuid4())
- self._client.open_connection(self._id, info=self._phoenix_props)
- def close(self):
- """Closes the connection.
- No further operations are allowed, either on the connection or any
- of its cursors, once the connection is closed.
- If the connection is used in a ``with`` statement, this method will
- be automatically called at the end of the ``with`` block.
- """
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- for cursor_ref in self._cursors:
- cursor = cursor_ref()
- if cursor is not None and not cursor._closed:
- cursor.close()
- self._client.close_connection(self._id)
- self._client.close()
- self._closed = True
- @property
- def closed(self):
- """Read-only attribute specifying if the connection is closed or not."""
- return self._closed
- def commit(self):
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- self._client.commit(self._id)
- def rollback(self):
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- self._client.rollback(self._id)
- def cursor(self, cursor_factory=None):
- """Creates a new cursor.
- :param cursor_factory:
- This argument can be used to create non-standard cursors.
- The class returned must be a subclass of
- :class:`~phoenixdb.cursor.Cursor` (for example :class:`~phoenixdb.cursor.DictCursor`).
- A default factory for the connection can also be specified using the
- :attr:`cursor_factory` attribute.
- :returns:
- A :class:`~phoenixdb.cursor.Cursor` object.
- """
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- cursor = (cursor_factory or self.cursor_factory)(self)
- self._cursors.append(weakref.ref(cursor, self._cursors.remove))
- return cursor
- def set_session(self, **props):
- """Sets one or more parameters in the current connection.
- :param autocommit:
- Switch the connection to autocommit mode.
- :param readonly:
- Switch the connection to read-only mode.
- """
- props = Connection._map_legacy_avatica_props(props)
- self._avatica_props = self._client.connection_sync_dict(self._id, props)
- @property
- def autocommit(self):
- """Read/write attribute for switching the connection's autocommit mode."""
- return self._avatica_props['autoCommit']
- @autocommit.setter
- def autocommit(self, value):
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- self._avatica_props = self._client.connection_sync_dict(self._id, {'autoCommit': bool(value)})
- @property
- def readonly(self):
- """Read/write attribute for switching the connection's readonly mode."""
- return self._avatica_props['readOnly']
- @readonly.setter
- def readonly(self, value):
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- self._avatica_props = self._client.connection_sync_dict(self._id, {'readOnly': bool(value)})
- @property
- def transactionisolation(self):
- return self._avatica_props['_transactionIsolation']
- @transactionisolation.setter
- def transactionisolation(self, value):
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- self._avatica_props = self._client.connection_sync_dict(self._id, {'transactionIsolation': bool(value)})
- def meta(self):
- """Creates a new meta.
- :returns:
- A :class:`~phoenixdb.meta` object.
- """
- if self._closed:
- raise ProgrammingError('The connection is already closed.')
- meta = Meta(self)
- return meta
- for name in errors.__all__:
- setattr(Connection, name, getattr(errors, name))
|