diff --git a/NEWS b/NEWS index d236eee6..8310badb 100644 --- a/NEWS +++ b/NEWS @@ -15,18 +15,23 @@ What's new in psycopg 2.5.3 - Work around `pip issue #1630 `__ making installation via ``pip -e git+url`` impossible (:ticket:`#18`). +- It is now possible to call `get_transaction_status()` on closed connections. +- Fixed unsafe access to object names causing assertion failures in + Python 3 debug builds (:ticket:`#188`). +- Mark the connection closed if found broken on `poll()` (from :ticket:`#192` + discussion) +- Fixed handling of dsn and closed attributes in connection subclasses + failing to connect (from :ticket:`#192` discussion). - Added arbitrary but stable order to `Range` objects, thanks to Chris Withers (:ticket:`#193`). - Avoid blocking async connections on connect (:ticket:`#194`). Thanks to Adam Petrovich for the bug report and diagnosis. -- Fixed unsafe access to object names causing assertion failures in - Python 3 debug builds (:ticket:`#188`). -- Fixed handling of dsn and closed attributes in connection subclasses - failing to connect (from :ticket:`#192` discussion). -- Fixed overflow opening a lobject with an oid not fitting in a signed int - (:ticket:`#203`). - Don't segfault using poorly defined cursor subclasses which forgot to call the superclass init (:ticket:`#195`). +- Mark the connection closed when a Socket connection is broken, as it + happens for TCP connections instead (:ticket:`#196`). +- Fixed overflow opening a lobject with an oid not fitting in a signed int + (:ticket:`#203`). - Fixed possible segfault in named cursors creation. - Fixed debug build on Windows, thanks to James Emerton. diff --git a/doc/src/connection.rst b/doc/src/connection.rst index eac2f001..2d483920 100644 --- a/doc/src/connection.rst +++ b/doc/src/connection.rst @@ -295,8 +295,8 @@ The ``connection`` class .. attribute:: closed - Read-only attribute reporting whether the database connection is open - (0) or closed (1). + Read-only integer attribute: 0 if the connection is open, nonzero if + it is closed or broken. .. method:: cancel diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 2de1d24d..cb47b662 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -700,8 +700,6 @@ psyco_conn_set_client_encoding(connectionObject *self, PyObject *args) static PyObject * psyco_conn_get_transaction_status(connectionObject *self) { - EXC_IF_CONN_CLOSED(self); - return PyInt_FromLong((long)PQtransactionStatus(self->pgconn)); } diff --git a/psycopg/lobject_int.c b/psycopg/lobject_int.c index 2f78e90b..43f2b179 100644 --- a/psycopg/lobject_int.c +++ b/psycopg/lobject_int.c @@ -253,7 +253,7 @@ lobject_close_locked(lobjectObject *self, char **error) return 0; break; default: - PyErr_SetString(OperationalError, "the connection is broken"); + *error = strdup("the connection is broken"); return -1; break; } diff --git a/psycopg/lobject_type.c b/psycopg/lobject_type.c index 1af352e3..068923c9 100644 --- a/psycopg/lobject_type.c +++ b/psycopg/lobject_type.c @@ -355,7 +355,7 @@ lobject_dealloc(PyObject* obj) { lobjectObject *self = (lobjectObject *)obj; - if (self->conn) { /* if not, init failed */ + if (self->conn && self->fd != -1) { if (lobject_close(self) < 0) PyErr_Print(); Py_XDECREF((PyObject*)self->conn); diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c index 86709657..a19c05a8 100644 --- a/psycopg/pqpath.c +++ b/psycopg/pqpath.c @@ -417,10 +417,20 @@ pq_complete_error(connectionObject *conn, PGresult **pgres, char **error) pq_raise(conn, NULL, pgres); /* now *pgres is null */ } - else if (*error != NULL) { - PyErr_SetString(OperationalError, *error); - } else { - PyErr_SetString(OperationalError, "unknown error"); + else { + if (*error != NULL) { + PyErr_SetString(OperationalError, *error); + } else { + PyErr_SetString(OperationalError, "unknown error"); + } + /* Trivia: with a broken socket connection PQexec returns NULL, so we + * end up here. With a TCP connection we get a pgres with an error + * instead, and the connection gets closed in the pq_raise call above + * (see ticket #196) + */ + if (CONNECTION_BAD == PQstatus(conn->pgconn)) { + conn->closed = 2; + } } if (*error) { @@ -797,6 +807,12 @@ pq_is_busy(connectionObject *conn) Dprintf("pq_is_busy: PQconsumeInput() failed"); pthread_mutex_unlock(&(conn->lock)); Py_BLOCK_THREADS; + + /* if the libpq says pgconn is lost, close the py conn */ + if (CONNECTION_BAD == PQstatus(conn->pgconn)) { + conn->closed = 2; + } + PyErr_SetString(OperationalError, PQerrorMessage(conn->pgconn)); return -1; } @@ -826,6 +842,12 @@ pq_is_busy_locked(connectionObject *conn) if (PQconsumeInput(conn->pgconn) == 0) { Dprintf("pq_is_busy_locked: PQconsumeInput() failed"); + + /* if the libpq says pgconn is lost, close the py conn */ + if (CONNECTION_BAD == PQstatus(conn->pgconn)) { + conn->closed = 2; + } + PyErr_SetString(OperationalError, PQerrorMessage(conn->pgconn)); return -1; } diff --git a/tests/test_lobject.py b/tests/test_lobject.py index beb277b2..66a3d8a1 100755 --- a/tests/test_lobject.py +++ b/tests/test_lobject.py @@ -130,6 +130,7 @@ class LargeObjectTests(LargeObjectTestCase): self.assertRaises(psycopg2.OperationalError, self.conn.lobject, 0, "w", lo.oid) + self.assert_(not self.conn.closed) def test_import(self): self.tmpdir = tempfile.mkdtemp()