Merge remote-tracking branch 'piro/devel' into devel

This commit is contained in:
Federico Di Gregorio 2011-08-10 18:36:24 +02:00
commit a59d88c703
7 changed files with 50 additions and 6 deletions

12
NEWS
View File

@ -1,3 +1,15 @@
What's new in psycopg 2.4.3
---------------------------
- Fixed segfault in case of transaction started with connection lost
(and possibly other events).
- Rollback connections in transaction or in error before putting them
back into a pool. Also discard broken connections (ticket #62).
- Lazy import of the slow uuid module, thanks to Marko Kreen.
- Fixed NamedTupleCursor.executemany() (ticket #65).
- Fixed --static-libpq setup option (ticket #64).
What's new in psycopg 2.4.2 What's new in psycopg 2.4.2
--------------------------- ---------------------------

View File

@ -26,10 +26,12 @@ directly into the client application.
Get a free connection and assign it to *key* if not `!None`. Get a free connection and assign it to *key* if not `!None`.
.. method:: putconn(conn, key=None) .. method:: putconn(conn, key=None, close=False)
Put away a connection. Put away a connection.
If *close* is `!True`, discard the connection from the pool.
.. method:: closeall .. method:: closeall
Close all the connections handled by the pool. Close all the connections handled by the pool.

View File

@ -275,7 +275,7 @@ class NamedTupleCursor(_cursor):
def executemany(self, query, vars): def executemany(self, query, vars):
self.Record = None self.Record = None
return _cursor.executemany(self, vars) return _cursor.executemany(self, query, vars)
def callproc(self, procname, vars=None): def callproc(self, procname, vars=None):
self.Record = None self.Record = None

View File

@ -25,6 +25,7 @@ This module implements thread-safe (and not) connection pools.
# License for more details. # License for more details.
import psycopg2 import psycopg2
import psycopg2.extensions as _ext
try: try:
import logging import logging
@ -115,13 +116,27 @@ class AbstractConnectionPool(object):
def _putconn(self, conn, key=None, close=False): def _putconn(self, conn, key=None, close=False):
"""Put away a connection.""" """Put away a connection."""
if self.closed: raise PoolError("connection pool is closed") if self.closed: raise PoolError("connection pool is closed")
if key is None: key = self._rused[id(conn)] if key is None: key = self._rused.get(id(conn))
if not key: if not key:
raise PoolError("trying to put unkeyed connection") raise PoolError("trying to put unkeyed connection")
if len(self._pool) < self.minconn and not close: if len(self._pool) < self.minconn and not close:
self._pool.append(conn) # Return the connection into a consistent state before putting
# it back into the pool
if not conn.closed:
status = conn.get_transaction_status()
if status == _ext.TRANSACTION_STATUS_UNKNOWN:
# server connection lost
conn.close()
elif status != _ext.TRANSACTION_STATUS_IDLE:
# connection in error or in transaction
conn.rollback()
self._pool.append(conn)
else:
# regular idle connection
self._pool.append(conn)
# If the connection is closed, we just discard it.
else: else:
conn.close() conn.close()

View File

@ -344,11 +344,13 @@ pq_execute_command_locked(connectionObject *conn, const char *query,
} }
if (*pgres == NULL) { if (*pgres == NULL) {
Dprintf("pq_execute_command_locked: PQexec returned NULL"); Dprintf("pq_execute_command_locked: PQexec returned NULL");
PyEval_RestoreThread(*tstate);
if (!PyErr_Occurred()) { if (!PyErr_Occurred()) {
const char *msg; const char *msg;
msg = PQerrorMessage(conn->pgconn); msg = PQerrorMessage(conn->pgconn);
if (msg && *msg) { *error = strdup(msg); } if (msg && *msg) { *error = strdup(msg); }
} }
*tstate = PyEval_SaveThread();
goto cleanup; goto cleanup;
} }
@ -635,11 +637,13 @@ pq_get_guc_locked(
if (*pgres == NULL) { if (*pgres == NULL) {
Dprintf("pq_get_guc_locked: PQexec returned NULL"); Dprintf("pq_get_guc_locked: PQexec returned NULL");
PyEval_RestoreThread(*tstate);
if (!PyErr_Occurred()) { if (!PyErr_Occurred()) {
const char *msg; const char *msg;
msg = PQerrorMessage(conn->pgconn); msg = PQerrorMessage(conn->pgconn);
if (msg && *msg) { *error = strdup(msg); } if (msg && *msg) { *error = strdup(msg); }
} }
*tstate = PyEval_SaveThread();
goto cleanup; goto cleanup;
} }
if (PQresultStatus(*pgres) != PGRES_TUPLES_OK) { if (PQresultStatus(*pgres) != PGRES_TUPLES_OK) {

View File

@ -73,7 +73,7 @@ except ImportError:
# Take a look at http://www.python.org/dev/peps/pep-0386/ # Take a look at http://www.python.org/dev/peps/pep-0386/
# for a consistent versioning pattern. # for a consistent versioning pattern.
PSYCOPG_VERSION = '2.4.2' PSYCOPG_VERSION = '2.4.3.dev0'
version_flags = ['dt', 'dec'] version_flags = ['dt', 'dec']
@ -362,7 +362,7 @@ class psycopg_build_ext(build_ext):
self.include_dirs.append(".") self.include_dirs.append(".")
if self.static_libpq: if self.static_libpq:
if not hasattr(self, 'link_objects'): if not getattr(self, 'link_objects', None):
self.link_objects = [] self.link_objects = []
self.link_objects.append( self.link_objects.append(
os.path.join(pg_config_helper.query("libdir"), "libpq.a")) os.path.join(pg_config_helper.query("libdir"), "libpq.a"))

View File

@ -171,6 +171,17 @@ class NamedTupleCursorTest(unittest.TestCase):
self.assertEqual(res[2].i, 3) self.assertEqual(res[2].i, 3)
self.assertEqual(res[2].s, 'baz') self.assertEqual(res[2].s, 'baz')
@skip_if_no_namedtuple
def test_executemany(self):
curs = self.conn.cursor()
curs.executemany("delete from nttest where i = %s",
[(1,), (2,)])
curs.execute("select * from nttest order by 1")
res = curs.fetchall()
self.assertEqual(1, len(res))
self.assertEqual(res[0].i, 3)
self.assertEqual(res[0].s, 'baz')
@skip_if_no_namedtuple @skip_if_no_namedtuple
def test_iter(self): def test_iter(self):
curs = self.conn.cursor() curs = self.conn.cursor()