Skip to content

Commit f7c8ad9

Browse files
committed
Factor the PG connection usability stuff into driver adapters.
1 parent 78a7415 commit f7c8ad9

2 files changed

Lines changed: 65 additions & 72 deletions

File tree

peewee-stubs/__init__.pyi

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,9 @@ class SqliteDatabase(Database):
970970
class _BasePsycopgAdapter:
971971
isolation_levels: dict[int, str]
972972
isolation_levels_inv: dict[str, int]
973+
txn_idle: Incomplete
974+
txn_inerror: Incomplete
975+
txn_unknown: Incomplete
973976
def __init__(self) -> None: ...
974977

975978
@overload
@@ -982,6 +985,10 @@ class _BasePsycopgAdapter:
982985
@overload
983986
def isolation_level_str(self, isolation_level: _T) -> _T: ...
984987

988+
def is_connection_usable(self, conn) -> bool: ...
989+
def is_connection_reusable(self, conn) -> bool: ...
990+
def is_connection_closed(self, conn) -> bool: ...
991+
985992
class Psycopg2Adapter(_BasePsycopgAdapter):
986993
json_type: Incomplete
987994
jsonb_type: Incomplete
@@ -991,9 +998,8 @@ class Psycopg2Adapter(_BasePsycopgAdapter):
991998
def get_binary_type(self) -> type[Incomplete]: ...
992999
def connect(self, db, **params): ...
9931000
def get_server_version(self, conn): ...
994-
def is_connection_usable(self, conn) -> bool: ...
995-
def is_connection_reusable(self, conn) -> bool: ...
996-
def is_connection_closed(self, conn) -> bool: ...
1001+
def txn_status(self, conn) -> int: ...
1002+
def rollback(self, conn) -> None: ...
9971003
def server_side_cursor(self, conn): ...
9981004

9991005
class Psycopg3Adapter(_BasePsycopgAdapter):
@@ -1005,9 +1011,8 @@ class Psycopg3Adapter(_BasePsycopgAdapter):
10051011
def get_binary_type(self) -> type[Incomplete]: ...
10061012
def connect(self, db, **params): ...
10071013
def get_server_version(self, conn): ...
1008-
def is_connection_usable(self, conn) -> bool: ...
1009-
def is_connection_reusable(self, conn) -> bool: ...
1010-
def is_connection_closed(self, conn) -> bool: ...
1014+
def txn_status(self, conn) -> int: ...
1015+
def rollback(self, conn) -> None: ...
10111016
def server_side_cursor(self, conn): ...
10121017

10131018
class PostgresqlDatabase(Database):

peewee.py

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4541,6 +4541,7 @@ def from_timestamp(self, date_field):
45414541

45424542
class _BasePsycopgAdapter(object):
45434543
isolation_levels = {} # Map int -> str.
4544+
txn_idle = txn_inerror = txn_unknown = None # Driver constants.
45444545

45454546
def __init__(self):
45464547
self.isolation_levels_inv = {
@@ -4556,6 +4557,37 @@ def isolation_level_str(self, isolation_level):
45564557
return self.isolation_levels[isolation_level]
45574558
return isolation_level
45584559

4560+
def is_connection_usable(self, conn):
4561+
return self.txn_status(conn) < self.txn_inerror
4562+
4563+
def is_connection_reusable(self, conn):
4564+
# If the status is unknown then we lost the connection to the server
4565+
# and the connection should not be re-used.
4566+
status = self.txn_status(conn)
4567+
if status == self.txn_unknown:
4568+
return False
4569+
elif status != self.txn_idle:
4570+
try:
4571+
self.rollback(conn)
4572+
except Exception:
4573+
return False
4574+
return True
4575+
4576+
def is_connection_closed(self, conn):
4577+
status = self.txn_status(conn)
4578+
if status == self.txn_unknown:
4579+
return True
4580+
try:
4581+
if status != self.txn_idle:
4582+
self.rollback(conn)
4583+
else:
4584+
# status flag is local, have to round trip.
4585+
conn.cursor().execute('SELECT 1')
4586+
except Exception:
4587+
return True
4588+
return False
4589+
4590+
45594591
class Psycopg2Adapter(_BasePsycopgAdapter):
45604592
isolation_levels = {
45614593
1: 'READ COMMITTED',
@@ -4564,6 +4596,11 @@ class Psycopg2Adapter(_BasePsycopgAdapter):
45644596
4: 'READ UNCOMMITTED',
45654597
}
45664598

4599+
if psycopg2 is not None:
4600+
txn_idle = pg_extensions.TRANSACTION_STATUS_IDLE
4601+
txn_inerror = pg_extensions.TRANSACTION_STATUS_INERROR
4602+
txn_unknown = pg_extensions.TRANSACTION_STATUS_UNKNOWN
4603+
45674604
def __init__(self):
45684605
super(Psycopg2Adapter, self).__init__()
45694606
self.json_type = Json_pg2
@@ -4594,44 +4631,17 @@ def connect(self, db, **params):
45944631
def get_server_version(self, conn):
45954632
return conn.server_version
45964633

4597-
def is_connection_usable(self, conn):
4598-
txn_status = conn.get_transaction_status()
4599-
return txn_status < pg_extensions.TRANSACTION_STATUS_INERROR
4634+
def txn_status(self, conn):
4635+
return conn.get_transaction_status()
46004636

4601-
def is_connection_reusable(self, conn):
4602-
# If the status is unknown then we lost the connection to the server
4603-
# and the connection should not be re-used.
4604-
txn_status = conn.get_transaction_status()
4605-
if txn_status == pg_extensions.TRANSACTION_STATUS_UNKNOWN:
4606-
return False
4607-
elif txn_status != pg_extensions.TRANSACTION_STATUS_IDLE:
4608-
# rollback() no-ops and reset() raises under autocommit, send a
4609-
# raw ROLLBACK (clears both in-txn and error states).
4610-
try:
4611-
conn.cursor().execute('ROLLBACK')
4612-
except Exception:
4613-
return False
4614-
return True
4615-
4616-
def is_connection_closed(self, conn):
4617-
txn_status = conn.get_transaction_status()
4618-
if txn_status == pg_extensions.TRANSACTION_STATUS_UNKNOWN:
4619-
return True
4620-
try:
4621-
if txn_status != pg_extensions.TRANSACTION_STATUS_IDLE:
4622-
# rollback() no-ops under autocommit, send a raw ROLLBACK.
4623-
conn.cursor().execute('ROLLBACK')
4624-
else:
4625-
# The status flag is local, only a round trip can detect a
4626-
# server-side disconnect.
4627-
conn.cursor().execute('SELECT 1')
4628-
except Exception:
4629-
return True
4630-
return False
4637+
def rollback(self, conn):
4638+
# rollback() no-ops and reset() raises under autocommit, send a raw
4639+
# ROLLBACK (clears both in-txn and error states).
4640+
conn.cursor().execute('ROLLBACK')
46314641

46324642
def server_side_cursor(self, conn):
46334643
# psycopg2 does not allow named cursors in autocommit, even if we ARE
4634-
# inside a transaction - so specify withhold (not desirable!).
4644+
# inside a transaction. Specify withhold (not desirable!).
46354645
return conn.cursor(name=str(uuid.uuid1()), withhold=True)
46364646

46374647

@@ -4642,6 +4652,10 @@ class Psycopg3Adapter(_BasePsycopgAdapter):
46424652
3: 'REPEATABLE READ',
46434653
4: 'SERIALIZABLE',
46444654
}
4655+
if psycopg is not None:
4656+
txn_idle = TransactionStatus.IDLE
4657+
txn_inerror = TransactionStatus.INERROR
4658+
txn_unknown = TransactionStatus.UNKNOWN
46454659

46464660
def __init__(self):
46474661
super(Psycopg3Adapter, self).__init__()
@@ -4668,43 +4682,17 @@ def connect(self, db, **params):
46684682
def get_server_version(self, conn):
46694683
return conn.pgconn.server_version
46704684

4671-
def is_connection_usable(self, conn):
4672-
return conn.pgconn.transaction_status < TransactionStatus.INERROR
4673-
4674-
def is_connection_reusable(self, conn):
4675-
# If the status is unknown then we lost the connection to the server
4676-
# and the connection should not be re-used.
4677-
txn_status = conn.pgconn.transaction_status
4678-
if txn_status == TransactionStatus.UNKNOWN:
4679-
return False
4680-
elif txn_status != TransactionStatus.IDLE:
4681-
# rollback() clears both in-txn and error states (psycopg3 has
4682-
# no Connection.reset()).
4683-
try:
4684-
conn.rollback()
4685-
except Exception:
4686-
return False
4687-
return True
4685+
def txn_status(self, conn):
4686+
return conn.pgconn.transaction_status
46884687

4689-
def is_connection_closed(self, conn):
4690-
txn_status = conn.pgconn.transaction_status
4691-
if txn_status == TransactionStatus.UNKNOWN:
4692-
return True
4693-
try:
4694-
if txn_status != TransactionStatus.IDLE:
4695-
conn.rollback()
4696-
else:
4697-
# The status flag is local, only a round trip can detect a
4698-
# server-side disconnect.
4699-
conn.execute('SELECT 1')
4700-
except Exception:
4701-
return True
4702-
return False
4688+
def rollback(self, conn):
4689+
# rollback() clears both in-txn and error states.
4690+
conn.rollback()
47034691

47044692
def server_side_cursor(self, conn):
47054693
# In a transaction a plain named cursor streams and is scoped to it.
47064694
# Otherwise the server requires withhold, which spools at declare.
4707-
in_txn = conn.pgconn.transaction_status == TransactionStatus.INTRANS
4695+
in_txn = self.txn_status(conn) == TransactionStatus.INTRANS
47084696
return conn.cursor(name=str(uuid.uuid1()), withhold=not in_txn)
47094697

47104698

0 commit comments

Comments
 (0)