connection.py 5.66 KB
Newer Older
Lukáš Lalinský's avatar
Lukáš Lalinský committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# Copyright 2015 Lukas Lalinsky
#
# 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.

Lukáš Lalinský's avatar
Lukáš Lalinský committed
15 16 17 18
import logging
import uuid
import weakref
from phoenixdb import errors
19
from phoenixdb.avatica import OPEN_CONNECTION_PROPERTIES
Lukáš Lalinský's avatar
Lukáš Lalinský committed
20 21 22 23 24 25 26 27 28 29
from phoenixdb.cursor import Cursor
from phoenixdb.errors import OperationalError, NotSupportedError, ProgrammingError

__all__ = ['Connection']

logger = logging.getLogger(__name__)


class Connection(object):
    """Database connection.
30

Lukáš Lalinský's avatar
Lukáš Lalinský committed
31 32 33 34 35 36 37
    You should not construct this object manually, use :func:`~phoenixdb.connect` instead.
    """

    def __init__(self, client, **kwargs):
        self._client = client
        self._closed = False
        self._cursors = []
38 39 40 41 42 43 44 45 46
        # Extract properties to pass to OpenConnectionRequest
        self._connection_args = {}
        # The rest of the kwargs
        self._filtered_args = {}
        for k in kwargs:
            if k in OPEN_CONNECTION_PROPERTIES:
                self._connection_args[k] = kwargs[k]
            else:
                self._filtered_args[k] = kwargs[k]
47
        self.open()
48
        self.set_session(**self._filtered_args)
Lukáš Lalinský's avatar
Lukáš Lalinský committed
49 50 51 52 53 54 55 56 57 58 59 60

    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()

61 62 63
    def open(self):
        """Opens the connection."""
        self._id = str(uuid.uuid4())
64
        self._client.openConnection(self._id, info=self._connection_args)
65

Lukáš Lalinský's avatar
Lukáš Lalinský committed
66 67 68
    def close(self):
        """Closes the connection.
        No further operations are allowed, either on the connection or any
69 70 71 72
        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.
Lukáš Lalinský's avatar
Lukáš Lalinský committed
73 74 75 76 77 78 79 80 81 82 83
        """
        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.closeConnection(self._id)
        self._client.close()
        self._closed = True

84 85 86 87 88
    @property
    def closed(self):
        """Read-only attribute specifying if the connection is closed or not."""
        return self._closed

Lukáš Lalinský's avatar
Lukáš Lalinský committed
89 90 91 92 93 94 95
    def commit(self):
        """Commits pending database changes.

        Currently, this does nothing, because the RPC does not support
        transactions. Only defined for DB API 2.0 compatibility.
        You need to use :attr:`autocommit` mode.
        """
96
        # TODO can support be added for this?
Lukáš Lalinský's avatar
Lukáš Lalinský committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
        if self._closed:
            raise ProgrammingError('the connection is already closed')

    def cursor(self):
        """Creates a new cursor.

        :returns:
            A :class:`~phoenixdb.cursor.Cursor` object.
        """
        if self._closed:
            raise ProgrammingError('the connection is already closed')
        cursor = Cursor(self)
        self._cursors.append(weakref.ref(cursor, self._cursors.remove))
        return cursor

    def set_session(self, autocommit=None, readonly=None):
        """Sets one or more parameters in the current connection.
114

Lukáš Lalinský's avatar
Lukáš Lalinský committed
115 116 117 118
        :param autocommit:
            Switch the connection to autocommit mode. With the current
            version, you need to always enable this, because
            :meth:`commit` is not implemented.
119

Lukáš Lalinský's avatar
Lukáš Lalinský committed
120 121 122 123 124 125 126 127 128
        :param readonly:
            Switch the connection to read-only mode.
        """
        props = {}
        if autocommit is not None:
            props['autoCommit'] = bool(autocommit)
        if readonly is not None:
            props['readOnly'] = bool(readonly)
        props = self._client.connectionSync(self._id, props)
Mark Heppner's avatar
Mark Heppner committed
129 130 131
        self._autocommit = props.auto_commit
        self._readonly = props.read_only
        self._transactionisolation = props.transaction_isolation
Lukáš Lalinský's avatar
Lukáš Lalinský committed
132 133 134

    @property
    def autocommit(self):
135
        """Read/write attribute for switching the connection's autocommit mode."""
Lukáš Lalinský's avatar
Lukáš Lalinský committed
136 137 138 139 140 141 142
        return self._autocommit

    @autocommit.setter
    def autocommit(self, value):
        if self._closed:
            raise ProgrammingError('the connection is already closed')
        props = self._client.connectionSync(self._id, {'autoCommit': bool(value)})
Mark Heppner's avatar
Mark Heppner committed
143
        self._autocommit = props.auto_commit
Lukáš Lalinský's avatar
Lukáš Lalinský committed
144 145 146

    @property
    def readonly(self):
147
        """Read/write attribute for switching the connection's readonly mode."""
Lukáš Lalinský's avatar
Lukáš Lalinský committed
148 149 150 151 152 153 154
        return self._readonly

    @readonly.setter
    def readonly(self, value):
        if self._closed:
            raise ProgrammingError('the connection is already closed')
        props = self._client.connectionSync(self._id, {'readOnly': bool(value)})
Mark Heppner's avatar
Mark Heppner committed
155
        self._readonly = props.read_only
Lukáš Lalinský's avatar
Lukáš Lalinský committed
156

157 158 159 160 161 162 163 164 165 166
    @property
    def transactionisolation(self):
        return self._transactionisolation

    @readonly.setter
    def transactionisolation(self, value):
        if self._closed:
            raise ProgrammingError('the connection is already closed')
        props = self._client.connectionSync(self._id, {'transactionIsolation': bool(value)})
        self._transactionisolation = props.transaction_isolation
Lukáš Lalinský's avatar
Lukáš Lalinský committed
167 168 169

for name in errors.__all__:
    setattr(Connection, name, getattr(errors, name))