Skip to content

Commit d214f73

Browse files
author
James William Pye
committed
Add clone() methods.
1 parent 8709585 commit d214f73

3 files changed

Lines changed: 61 additions & 0 deletions

File tree

postgresql/api.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ class Cursor(
287287
False : 'BACKWARD',
288288
}
289289

290+
@abstractmethod
291+
def clone(self) -> "Cursor":
292+
"""
293+
Create a new cursor using the same factors as `self`.
294+
"""
295+
290296
def __iter__(self):
291297
return self
292298

@@ -461,6 +467,15 @@ def parameter_types(self) -> [type]:
461467
[<class 'int'>, <class 'str'>]
462468
"""
463469

470+
@abstractmethod
471+
def clone(self) -> "PreparedStatement":
472+
"""
473+
Create a new statement object using the same factors as `self`.
474+
475+
When used for refreshing plans, the new clone should replace references to
476+
the original.
477+
"""
478+
464479
@abstractmethod
465480
def __call__(self, *parameters : "Positional Parameters") -> ["Row"]:
466481
"""
@@ -1167,6 +1182,13 @@ def closed(self) -> bool:
11671182
True
11681183
"""
11691184

1185+
@abstractmethod
1186+
def clone(self) -> "Connection":
1187+
"""
1188+
Create another connection using the same factors as `self`. The returned
1189+
object should be open and ready for use.
1190+
"""
1191+
11701192
def connect(self) -> None:
11711193
"""
11721194
Establish the connection to the server and initialize the category.

postgresql/driver/pq3.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,9 @@ def _e_metas(self):
480480
yield ('direction', 'FORWARD' if self.direction else 'BACKWORD')
481481
yield ('type', 'Cursor')
482482

483+
def clone(self):
484+
return type(self)(self.statement, self.parameters, self.database, None)
485+
483486
def __init__(self, statement, parameters, database, cursor_id):
484487
self.database = database or statement.database
485488
self.statement = statement
@@ -667,6 +670,12 @@ def _e_metas(self):
667670
elif ct is not None:
668671
yield ('sql_column_types', ct)
669672

673+
def clone(self):
674+
ps = type(self)(self.database, None, self.string)
675+
ps._init()
676+
ps._fini()
677+
return ps
678+
670679
def __init__(self, database, statement_id, string):
671680
self.database = database
672681
self.statement_id = statement_id or ID(self)
@@ -2074,6 +2083,11 @@ def _receive_async(self, msg, controller = None):
20742083
creator = c
20752084
).raise_message()
20762085

2086+
def clone(self, *args, **kw):
2087+
c = type(self)(self.connector, *args, **kw)
2088+
c.connect()
2089+
return c
2090+
20772091
def __init__(self, connector, *args, **kw):
20782092
"""
20792093
Create a connection based on the given connector.

postgresql/test/test_driver.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,31 @@ def raise_exc(l):
330330
raise v
331331
self.failUnlessRaises(pg_exc.QueryCanceledError, raise_exc, rl)
332332

333+
def testClones(self):
334+
self.db.execute('create table _can_clone_see_this (i int);')
335+
try:
336+
with self.db.clone() as db2:
337+
self.failUnlessEqual(db2.prepare('select 1').first(), 1)
338+
self.failUnlessEqual(db2.prepare(
339+
"select count(*) FROM information_schema.tables " \
340+
"where table_name = '_can_clone_see_this'"
341+
).first(), 1
342+
)
343+
finally:
344+
self.db.execute('drop table _can_clone_see_this')
345+
# check already open
346+
db = self.db.clone()
347+
self.failUnlessEqual(db.prepare('select 1').first(), 1)
348+
db.close()
349+
350+
ps = self.db.prepare('select 1')
351+
ps2 = ps.clone()
352+
self.failUnlessEqual(ps2.first(), ps.first())
353+
ps2.close()
354+
c = ps.declare()
355+
c2 = c.clone()
356+
self.failUnlessEqual(c.read(), c2.read())
357+
333358
def testItsClosed(self):
334359
ps = self.db.prepare("SELECT 1")
335360
# If scroll is False it will pre-fetch, and no error will be thrown.

0 commit comments

Comments
 (0)