Skip to content

Commit 5d3835f

Browse files
author
James William Pye
committed
Implement DB-API as a subclass of PG-API.
DB-API's connect() will now inherit defaults similar to postgresql.open and libpq-based drivers. Should have been this way earlier, but the desire of a strict implementation intervened with any such production. This gives DB-API users direct access to PG-API interfaces.
1 parent 397aedc commit 5d3835f

5 files changed

Lines changed: 66 additions & 25 deletions

File tree

postgresql/cluster.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Cluster(pg_api.Cluster):
5656
to be a base class for managing a cluster, and is intended to be extended to
5757
accommodate for a particular purpose.
5858
"""
59+
driver = pg_driver.default
5960
installation = None
6061
data_directory = None
6162
DEFAULT_CLUSTER_ENCODING = DEFAULT_CLUSTER_ENCODING
@@ -413,7 +414,7 @@ def connector(self, **kw):
413414
listen_addresses and port configuration in settings.
414415
"""
415416
host, port = self.address()
416-
return pg_driver.default.fit(
417+
return self.driver.fit(
417418
host = host or 'localhost',
418419
port = port or 5432,
419420
**kw
@@ -472,7 +473,7 @@ def ready_for_connections(self):
472473
e = None
473474
host, port = self.address()
474475
try:
475-
pg_driver.connect(
476+
self.driver.connect(
476477
user = ' -*- ping -*- ',
477478
host = host or 'localhost',
478479
port = port or 5432,

postgresql/documentation/changes.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,32 @@
99
0.9.0
1010
-----
1111
12+
* Performance improvements.
1213
* Extend `postgresql.open` to take keyword arguments.
1314
* Implement query libraries and connection categories[#1010581 & #1010618].
1415
* Implement ``clone()`` on Connections, Statements, and Cursors.
15-
* Refactor ``ps.load()`` into ``ps.load_chunks()`` and ``ps.load_rows()``.
1616
* Display the line and mark the location of the POSITION for syntax errors.
17-
* Performance improvements.
17+
* Refactor ``ps.load()`` into ``ps.load_chunks()`` and ``ps.load_rows()``.
1818
* Refactor `postgresql.api.InterfaceElement`.
1919
* Refactor driver.pq3.Connection to use protocol.client3.Connection.
2020
* Refactor driver.pq3.Cursor into types selected by PreparedStatements.
2121
* Fix memory leak due to circular references and __del__. [Reported by Valentine Gogichashvili]
2222
Additionally, try to avoid circular references at that level.
23+
* Correct StoredProcedure's statement production. It was falsely using a
24+
a mechanism that would use SQL predefined type names instead of
25+
qualified names. [Reported by Dallas Morisett]
26+
* Make DB-API connect() inherit defaults like postgresql.open()
27+
* Make DB-API extend PG-API so that DB-API connections will now have direct
28+
access to PG-API features.
29+
30+
31+
0.8.2
32+
-----
33+
34+
* Correct StoredProcedure's statement production. It was falsely using a
35+
a mechanism that would use SQL predefined type names instead of
36+
qualified names. [Reported by Dallas Morisett]
37+
2338
2439
0.8.1 released on 2009-04-30
2540
----------------------------

postgresql/driver/dbapi20.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
import time
1616
import re
1717

18+
from .. import clientparameters as pg_param
1819
from .. import driver as pg_driver
1920
from .. import types as pg_type
2021
from .. import string as pg_str
22+
from .pq3 import Connection
2123

2224
##
2325
# Basically, is it a mapping, or is it a sequence?
@@ -82,6 +84,9 @@ def dbapi_type(typid):
8284
return ROWID
8385

8486
class Portal(object):
87+
"""
88+
Manages read() interfaces to a chunks iterator.
89+
"""
8590
def __init__(self, chunks):
8691
self.chunks = chunks
8792
self.buf = []
@@ -93,6 +98,10 @@ def __next__(self):
9398
self.pos += 1
9499
return r
95100
except IndexError:
101+
# Any alledged infinite recursion will stop on the StopIteration
102+
# thrown by this next(). Recursion is unlikely to occur more than
103+
# once; specifically, empty chunks would need to be returned
104+
# by this invocation of next().
96105
self.buf = next(self.chunks)
97106
self.pos = 0
98107
return self.__next__()
@@ -125,8 +134,7 @@ class Cursor(object):
125134
description = None
126135

127136
def __init__(self, C):
128-
self.connection = C
129-
self.database = C.database
137+
self.database = self.connection = C
130138
self.description = ()
131139
self.__portals = []
132140

@@ -173,6 +181,7 @@ def fetchone(self):
173181
def __next__(self):
174182
return next(self._portal)
175183
next = __next__
184+
176185
def __iter__(self):
177186
return self
178187

@@ -275,7 +284,7 @@ def close(self):
275284
self.__portals = None
276285
for p in ps: p.close()
277286

278-
class Connection(object):
287+
class Connection(Connection):
279288
"""
280289
DB-API 2.0 connection implementation for PG-API connection objects.
281290
"""
@@ -299,7 +308,7 @@ def autocommit_set(self, val):
299308
else:
300309
if self._xact is not None:
301310
return
302-
self._xact = self.database.xact()
311+
self._xact = self.xact()
303312
self._xact.start()
304313

305314
def autocommit_get(self):
@@ -315,19 +324,19 @@ def autocommit_del(self):
315324
)
316325
del autocommit_set, autocommit_get, autocommit_del
317326

318-
def __init__(self, connection):
319-
self.database = connection
320-
self._xact = self.database.xact()
327+
def connect(self, *args, **kw):
328+
super().connect(*args, **kw)
329+
self._xact = self.xact()
321330
self._xact.start()
322331

323332
def close(self):
324-
if self.database.closed:
333+
if self.closed:
325334
raise Error(
326335
"connection already closed",
327336
source = 'CLIENT',
328-
creator = self.database
337+
creator = self
329338
)
330-
self.database.close()
339+
super().close()
331340

332341
def cursor(self):
333342
return Cursor(self)
@@ -340,10 +349,10 @@ def commit(self):
340349
details = {
341350
'hint': 'The "autocommit" property on the connection was set to True.'
342351
},
343-
creator = self.database
352+
creator = self
344353
)
345354
self._xact.commit()
346-
self._xact = self.database.xact()
355+
self._xact = self.xact()
347356
self._xact.start()
348357

349358
def rollback(self):
@@ -354,16 +363,22 @@ def rollback(self):
354363
details = {
355364
'hint': 'The "autocommit" property on the connection was set to True.'
356365
},
357-
creator = self.database
366+
creator = self
358367
)
359368
self._xact.rollback()
360-
self._xact = self.database.xact()
369+
self._xact = self.xact()
361370
self._xact.start()
362371

372+
driver = pg_driver.Driver(connection = Connection)
363373
def connect(**kw):
364374
"""
365375
Create a DB-API connection using the given parameters.
366376
"""
367-
db = pg_driver.connect(**kw)
368-
dbapi = Connection(db)
369-
return dbapi
377+
std_params = pg_param.collect(prompt_title = None)
378+
params = pg_param.normalize(
379+
list(pg_param.denormalize_parameters(std_params)) + \
380+
list(pg_param.denormalize_parameters(kw))
381+
)
382+
# Resolve the password, but never prompt.
383+
pg_param.resolve_password(params, prompt_title = None)
384+
return driver.connect(**params)

postgresql/test/test_dbapi20.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import unittest
55
from ..unittest import TestCaseWithCluster
66
import time
7+
from contextlib import nested
78

89
##
910
# Various Adjustments for pg.driver.dbapi20
@@ -108,10 +109,16 @@ def tearDown(self):
108109
finally:
109110
con.close()
110111

112+
def connection(self):
113+
return nested()
114+
111115
def _connect(self):
112-
c = self.db.connector()
113-
c.connect()
114-
return self.driver.Connection(c)
116+
host, port = self.cluster.address()
117+
return self.driver.connect(
118+
user = 'test',
119+
host = host,
120+
port = port,
121+
)
115122

116123
def test_connect(self):
117124
con = self._connect()

postgresql/unittest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ def initialize_database(self):
6969
).first() is None:
7070
c.execute('create database test')
7171

72+
def connection(self, *args, **kw):
73+
return self.cluster.connection(*args, user = 'test', **kw)
74+
7275
def run(self, *args, **kw):
7376
if not self.cluster.initialized():
7477
self.cluster.encoding = 'utf-8'
@@ -91,7 +94,7 @@ def run(self, *args, **kw):
9194
self.cluster.start()
9295
self.cluster.wait_until_started()
9396

94-
db = self.cluster.connection(user = 'test',)
97+
db = self.connection()
9598
with db:
9699
self.db = db
97100
return super().run(*args, **kw)

0 commit comments

Comments
 (0)