Fixed infinite loop in pq_get_last_result after COPY

There will be an error downstream but we have to get out of this
function first.

Close #781
This commit is contained in:
Daniele Varrazzo 2018-10-10 23:23:56 +01:00
parent c442b3ec46
commit c314512115
4 changed files with 23 additions and 5 deletions

3
NEWS
View File

@ -33,7 +33,8 @@ What's new in psycopg 2.7.6
- Close named cursors if exist, even if `~cursor.execute()` wasn't called
(:ticket:`#746`).
- Fixed building on modern FreeBSD versions with Python 3.7 (:ticket:`#755`)
- Fixed building on modern FreeBSD versions with Python 3.7 (:ticket:`#755`).
- Fixed hang trying to :sql:`COPY` via `~cursor.execute()` (:ticket:`#781`).
What's new in psycopg 2.7.5

View File

@ -1106,12 +1106,13 @@ pq_send_query(connectionObject *conn, const char *query)
* The function will block only if a command is active and the
* necessary response data has not yet been read by PQconsumeInput.
*
* The result should be disposed using PQclear()
* The result should be disposed of using PQclear()
*/
PGresult *
pq_get_last_result(connectionObject *conn)
{
PGresult *result = NULL, *res;
ExecStatusType status;
/* Read until PQgetResult gives a NULL */
while (NULL != (res = PQgetResult(conn->pgconn))) {
@ -1124,11 +1125,15 @@ pq_get_last_result(connectionObject *conn)
PQclear(result);
}
result = res;
status = PQresultStatus(result);
Dprintf("pq_get_last_result: got result %s", PQresStatus(status));
/* After entering copy both mode, libpq will make a phony
/* After entering copy mode, libpq will make a phony
* PGresult for us every time we query for it, so we need to
* break out of this endless loop. */
if (PQresultStatus(result) == PGRES_COPY_BOTH) {
if (status == PGRES_COPY_BOTH
|| status == PGRES_COPY_OUT
|| status == PGRES_COPY_IN) {
break;
}
}

View File

@ -450,6 +450,12 @@ class AsyncTests(ConnectingTestCase):
else:
self.fail("no exception raised")
@skip_before_postgres(8, 2)
def test_copy_no_hang(self):
cur = self.conn.cursor()
cur.execute("copy (select 1) to stdout")
self.assertRaises(psycopg2.ProgrammingError, self.wait, self.conn)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -27,7 +27,7 @@ import psycopg2
import psycopg2.extensions
import psycopg2.extras
from .testutils import ConnectingTestCase, slow
from .testutils import ConnectingTestCase, skip_before_postgres, slow
class ConnectionStub(object):
@ -111,6 +111,12 @@ class GreenTestCase(ConnectingTestCase):
curs.execute("select 1")
self.assertEqual(curs.fetchone()[0], 1)
@skip_before_postgres(8, 2)
def test_copy_no_hang(self):
cur = self.conn.cursor()
self.assertRaises(psycopg2.ProgrammingError,
cur.execute, "copy (select 1) to stdout")
class CallbackErrorTestCase(ConnectingTestCase):
def setUp(self):