Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 15 additions & 109 deletions src/connection.py
Original file line number Diff line number Diff line change
@@ -1,117 +1,23 @@
# coding: utf-8
# we support both pg8000 and psycopg2

# to backward compatibility
from .exceptions import NodeConnectionDatabaseException as DatabaseError
from .exceptions import NodeConnectionOperationalException as OperationalError
from .exceptions import NodeConnectionInternalException as InternalError
from .exceptions import NodeConnectionProgrammingException as ProgrammingError

try:
import psycopg2 as pglib
from .impl.connection_psycopg2 import NodeConnection, pglib
except ImportError:
try:
import pg8000 as pglib
from .impl.connection_pg8000 import NodeConnection, pglib
except ImportError:
raise ImportError("You must have psycopg2 or pg8000 modules installed")

from .enums import IsolationLevel

from .defaults import \
default_dbname, \
default_username2

from .exceptions import QueryException

# export some exceptions
DatabaseError = pglib.DatabaseError
InternalError = pglib.InternalError
ProgrammingError = pglib.ProgrammingError
OperationalError = pglib.OperationalError


class NodeConnection:
"""
Transaction wrapper returned by Node
"""
def __init__(self,
node,
dbname=None,
username=None,
password=None,
autocommit=False):

# Set default arguments
dbname = dbname or default_dbname()
username = username or default_username2(node.os_ops)

self._node = node

self._connection = pglib.connect(
database=dbname,
user=username,
password=password,
host=node.host,
port=node.port
)

self._connection.autocommit = autocommit
self._cursor = self.connection.cursor()

@property
def node(self):
return self._node

@property
def connection(self):
return self._connection

@property
def pid(self):
return self.execute("select pg_catalog.pg_backend_pid()")[0][0]

@property
def cursor(self):
return self._cursor

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
self.close()

def begin(self, isolation_level=IsolationLevel.ReadCommitted):
# Check if level isn't an IsolationLevel
if not isinstance(isolation_level, IsolationLevel):
# Get name of isolation level
level_str = str(isolation_level).lower()

# Validate level string
try:
isolation_level = IsolationLevel(level_str)
except ValueError:
error = 'Invalid isolation level "{}"'
raise QueryException(error.format(level_str))

# Set isolation level
cmd = 'SET TRANSACTION ISOLATION LEVEL {}'
self.cursor.execute(cmd.format(isolation_level.value))

return self

def commit(self):
self.connection.commit()

return self

def rollback(self):
self.connection.rollback()

return self

def execute(self, query, *args):
self.cursor.execute(query, args)

if self.cursor.description is None:
return None

# pg8000 might return tuples
res = [tuple(t) for t in self.cursor.fetchall()]
return res

def close(self):
self.cursor.close()
self.connection.close()
assert DatabaseError.__name__ == "NodeConnectionDatabaseException"
assert OperationalError.__name__ == "NodeConnectionOperationalException"
assert InternalError.__name__ == "NodeConnectionInternalException"
assert ProgrammingError.__name__ == "NodeConnectionProgrammingException"
assert NodeConnection.__name__ == "NodeConnection"
assert pglib is not None
79 changes: 79 additions & 0 deletions src/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,85 @@ def __repr__(self) -> str:
return result


class NodeConnectionException(TestgresException):
_message: typing.Optional[str]

def __init__(
self,
message: typing.Optional[str] = None,
):
assert message is None or type(message) is str
super().__init__(message)
self._message = message
return

@property
def message(self) -> str:
assert self._message is None or type(self._message) is str
if self._message is None:
return ""
return self._message

def __repr__(self) -> str:
args = []

if self._message is not None:
args.append(("message", self._message))

result = "{}(".format(type(self).__name__)
sep = ""
for a in args:
result += sep + a[0] + "=" + repr(a[1])
sep = ", "
continue
result += ")"
return result


class NodeConnectionInterfaceException(NodeConnectionException):
pass


class NodeConnectionDatabaseException(NodeConnectionException):
pass


class NodeConnectionDataException(NodeConnectionDatabaseException):
pass


class NodeConnectionOperationalException(NodeConnectionDatabaseException):
pass


class NodeConnectionIntegrityException(NodeConnectionDatabaseException):
pass


class NodeConnectionInternalException(NodeConnectionDatabaseException):
pass


class NodeConnectionProgrammingException(NodeConnectionDatabaseException):
pass


class NodeConnectionNotSupportedException(NodeConnectionDatabaseException):
pass


class NodeConnectionQueryCanceledException(NodeConnectionOperationalException):
pass


class NodeConnectionTransactionRollbackException(NodeConnectionOperationalException):
pass


class NodeConnectionArrayContentNotSupportedException(NodeConnectionNotSupportedException):
pass


assert ExecUtilException.__name__ == "ExecUtilException"
assert InvalidOperationException.__name__ == "InvalidOperationException"
assert ExecTimeoutException.__name__ == "ExecTimeoutException"
Loading
Loading