connection.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # Licensed to the Apache Software Foundation (ASF) under one or more
  2. # contributor license agreements. See the NOTICE file distributed with
  3. # this work for additional information regarding copyright ownership.
  4. # The ASF licenses this file to You under the Apache License, Version 2.0
  5. # (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import logging
  16. import uuid
  17. import weakref
  18. from phoenixdb import errors
  19. from phoenixdb.avatica.client import OPEN_CONNECTION_PROPERTIES
  20. from phoenixdb.cursor import Cursor
  21. from phoenixdb.errors import ProgrammingError
  22. __all__ = ['Connection']
  23. logger = logging.getLogger(__name__)
  24. class Connection(object):
  25. """Database connection.
  26. You should not construct this object manually, use :func:`~phoenixdb.connect` instead.
  27. """
  28. cursor_factory = None
  29. """
  30. The default cursor factory used by :meth:`cursor` if the parameter is not specified.
  31. """
  32. def __init__(self, client, cursor_factory=None, **kwargs):
  33. self._client = client
  34. self._closed = False
  35. if cursor_factory is not None:
  36. self.cursor_factory = cursor_factory
  37. else:
  38. self.cursor_factory = Cursor
  39. self._cursors = []
  40. # Extract properties to pass to OpenConnectionRequest
  41. self._connection_args = {}
  42. # The rest of the kwargs
  43. self._filtered_args = {}
  44. for k in kwargs:
  45. if k in OPEN_CONNECTION_PROPERTIES:
  46. self._connection_args[k] = kwargs[k]
  47. else:
  48. self._filtered_args[k] = kwargs[k]
  49. self.open()
  50. self.set_session(**self._filtered_args)
  51. def __del__(self):
  52. if not self._closed:
  53. self.close()
  54. def __enter__(self):
  55. return self
  56. def __exit__(self, exc_type, exc_value, traceback):
  57. if not self._closed:
  58. self.close()
  59. def open(self):
  60. """Opens the connection."""
  61. self._id = str(uuid.uuid4())
  62. self._client.open_connection(self._id, info=self._connection_args)
  63. def close(self):
  64. """Closes the connection.
  65. No further operations are allowed, either on the connection or any
  66. of its cursors, once the connection is closed.
  67. If the connection is used in a ``with`` statement, this method will
  68. be automatically called at the end of the ``with`` block.
  69. """
  70. if self._closed:
  71. raise ProgrammingError('the connection is already closed')
  72. for cursor_ref in self._cursors:
  73. cursor = cursor_ref()
  74. if cursor is not None and not cursor._closed:
  75. cursor.close()
  76. self._client.close_connection(self._id)
  77. self._client.close()
  78. self._closed = True
  79. @property
  80. def closed(self):
  81. """Read-only attribute specifying if the connection is closed or not."""
  82. return self._closed
  83. def commit(self):
  84. if self._closed:
  85. raise ProgrammingError('the connection is already closed')
  86. self._client.commit(self._id)
  87. def rollback(self):
  88. if self._closed:
  89. raise ProgrammingError('the connection is already closed')
  90. self._client.rollback(self._id)
  91. def cursor(self, cursor_factory=None):
  92. """Creates a new cursor.
  93. :param cursor_factory:
  94. This argument can be used to create non-standard cursors.
  95. The class returned must be a subclass of
  96. :class:`~phoenixdb.cursor.Cursor` (for example :class:`~phoenixdb.cursor.DictCursor`).
  97. A default factory for the connection can also be specified using the
  98. :attr:`cursor_factory` attribute.
  99. :returns:
  100. A :class:`~phoenixdb.cursor.Cursor` object.
  101. """
  102. if self._closed:
  103. raise ProgrammingError('the connection is already closed')
  104. cursor = (cursor_factory or self.cursor_factory)(self)
  105. self._cursors.append(weakref.ref(cursor, self._cursors.remove))
  106. return cursor
  107. def set_session(self, autocommit=None, readonly=None):
  108. """Sets one or more parameters in the current connection.
  109. :param autocommit:
  110. Switch the connection to autocommit mode.
  111. :param readonly:
  112. Switch the connection to read-only mode.
  113. """
  114. props = {}
  115. if autocommit is not None:
  116. props['autoCommit'] = bool(autocommit)
  117. if readonly is not None:
  118. props['readOnly'] = bool(readonly)
  119. props = self._client.connection_sync(self._id, props)
  120. self._autocommit = props.auto_commit
  121. self._readonly = props.read_only
  122. self._transactionisolation = props.transaction_isolation
  123. @property
  124. def autocommit(self):
  125. """Read/write attribute for switching the connection's autocommit mode."""
  126. return self._autocommit
  127. @autocommit.setter
  128. def autocommit(self, value):
  129. if self._closed:
  130. raise ProgrammingError('the connection is already closed')
  131. props = self._client.connection_sync(self._id, {'autoCommit': bool(value)})
  132. self._autocommit = props.auto_commit
  133. @property
  134. def readonly(self):
  135. """Read/write attribute for switching the connection's readonly mode."""
  136. return self._readonly
  137. @readonly.setter
  138. def readonly(self, value):
  139. if self._closed:
  140. raise ProgrammingError('the connection is already closed')
  141. props = self._client.connection_sync(self._id, {'readOnly': bool(value)})
  142. self._readonly = props.read_only
  143. @property
  144. def transactionisolation(self):
  145. return self._transactionisolation
  146. @transactionisolation.setter
  147. def transactionisolation(self, value):
  148. if self._closed:
  149. raise ProgrammingError('the connection is already closed')
  150. props = self._client.connection_sync(self._id, {'transactionIsolation': bool(value)})
  151. self._transactionisolation = props.transaction_isolation
  152. for name in errors.__all__:
  153. setattr(Connection, name, getattr(errors, name))