mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-01-31 09:24:07 +03:00
Correctly flush async queries in 'green' mode.
This commit is contained in:
parent
8ba0f00d21
commit
0dd5d3f1d9
|
@ -701,6 +701,7 @@ conn_poll_green(connectionObject *self)
|
||||||
Dprintf("conn_poll: status = CONN_STATUS_READY/BEGIN");
|
Dprintf("conn_poll: status = CONN_STATUS_READY/BEGIN");
|
||||||
switch (self->async_status) {
|
switch (self->async_status) {
|
||||||
case ASYNC_READ:
|
case ASYNC_READ:
|
||||||
|
Dprintf("conn_poll: async_status = ASYNC_READ");
|
||||||
if (0 == PQconsumeInput(self->pgconn)) {
|
if (0 == PQconsumeInput(self->pgconn)) {
|
||||||
PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn));
|
PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn));
|
||||||
res = PSYCO_POLL_ERROR;
|
res = PSYCO_POLL_ERROR;
|
||||||
|
@ -712,6 +713,22 @@ conn_poll_green(connectionObject *self)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ASYNC_WRITE:
|
||||||
|
Dprintf("conn_poll: async_status = ASYNC_WRITE");
|
||||||
|
switch (PQflush(self->pgconn)) {
|
||||||
|
case 0: /* success */
|
||||||
|
res = PSYCO_POLL_OK;
|
||||||
|
break;
|
||||||
|
case 1: /* would block */
|
||||||
|
res = PSYCO_POLL_WRITE;
|
||||||
|
break;
|
||||||
|
case -1: /* error */
|
||||||
|
PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn));
|
||||||
|
res = PSYCO_POLL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Dprintf("conn_poll: in unexpected async status: %d",
|
Dprintf("conn_poll: in unexpected async status: %d",
|
||||||
self->async_status);
|
self->async_status);
|
||||||
|
|
|
@ -144,7 +144,7 @@ psyco_exec_green(connectionObject *conn, const char *command)
|
||||||
{
|
{
|
||||||
PGconn *pgconn = conn->pgconn;
|
PGconn *pgconn = conn->pgconn;
|
||||||
PGresult *result = NULL, *res;
|
PGresult *result = NULL, *res;
|
||||||
PyObject *cb;
|
PyObject *cb, *pyrv;
|
||||||
|
|
||||||
if (!(cb = have_wait_callback())) {
|
if (!(cb = have_wait_callback())) {
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -153,18 +153,26 @@ psyco_exec_green(connectionObject *conn, const char *command)
|
||||||
/* Send the query asynchronously */
|
/* Send the query asynchronously */
|
||||||
Dprintf("psyco_exec_green: sending query async");
|
Dprintf("psyco_exec_green: sending query async");
|
||||||
if (0 == PQsendQuery(pgconn, command)) {
|
if (0 == PQsendQuery(pgconn, command)) {
|
||||||
/* TODO: not handling the case of block during send */
|
|
||||||
Dprintf("psyco_exec_green: PQsendQuery returned 0");
|
Dprintf("psyco_exec_green: PQsendQuery returned 0");
|
||||||
goto clear;
|
goto clear;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loop reading data using the user-provided wait function */
|
/* Ensure the query reached the server. */
|
||||||
conn->async_status = ASYNC_READ;
|
conn->async_status = ASYNC_WRITE;
|
||||||
PyObject *pyrv;
|
|
||||||
|
|
||||||
pyrv = PyObject_CallFunctionObjArgs(cb, conn, NULL, NULL);
|
pyrv = PyObject_CallFunctionObjArgs(cb, conn, NULL, NULL);
|
||||||
if (!pyrv) {
|
if (!pyrv) {
|
||||||
Dprintf("psyco_exec_green: error in callback");
|
Dprintf("psyco_exec_green: error in callback sending query");
|
||||||
|
goto clear;
|
||||||
|
}
|
||||||
|
Py_DECREF(pyrv);
|
||||||
|
|
||||||
|
/* Loop reading data using the user-provided wait function */
|
||||||
|
conn->async_status = ASYNC_READ;
|
||||||
|
|
||||||
|
pyrv = PyObject_CallFunctionObjArgs(cb, conn, NULL, NULL);
|
||||||
|
if (!pyrv) {
|
||||||
|
Dprintf("psyco_exec_green: error in callback reading result");
|
||||||
goto clear;
|
goto clear;
|
||||||
}
|
}
|
||||||
Py_DECREF(pyrv);
|
Py_DECREF(pyrv);
|
||||||
|
|
|
@ -37,6 +37,7 @@ import test_lobject
|
||||||
import test_copy
|
import test_copy
|
||||||
import test_notify
|
import test_notify
|
||||||
import test_async
|
import test_async
|
||||||
|
import test_green
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
@ -53,6 +54,7 @@ def test_suite():
|
||||||
suite.addTest(test_copy.test_suite())
|
suite.addTest(test_copy.test_suite())
|
||||||
suite.addTest(test_notify.test_suite())
|
suite.addTest(test_notify.test_suite())
|
||||||
suite.addTest(test_async.test_suite())
|
suite.addTest(test_async.test_suite())
|
||||||
|
suite.addTest(test_green.test_suite())
|
||||||
return suite
|
return suite
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
60
tests/test_green.py
Normal file
60
tests/test_green.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import psycopg2
|
||||||
|
import psycopg2.extensions
|
||||||
|
import psycopg2.extras
|
||||||
|
import tests
|
||||||
|
|
||||||
|
class ConnectionStub(object):
|
||||||
|
"""A `connection` wrapper allowing analysis of the `poll()` calls."""
|
||||||
|
def __init__(self, conn):
|
||||||
|
self.conn = conn
|
||||||
|
self.polls = []
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
return self.conn.fileno()
|
||||||
|
|
||||||
|
def poll(self):
|
||||||
|
rv = self.conn.poll()
|
||||||
|
self.polls.append(rv)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
class GreenTests(unittest.TestCase):
|
||||||
|
def connect(self):
|
||||||
|
return psycopg2.connect(tests.dsn)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self._cb = psycopg2.extensions.get_wait_callback()
|
||||||
|
psycopg2.extensions.set_wait_callback(psycopg2.extras.wait_select)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
psycopg2.extensions.set_wait_callback(self._cb)
|
||||||
|
|
||||||
|
def set_stub_wait_callback(self, conn):
|
||||||
|
stub = ConnectionStub(conn)
|
||||||
|
psycopg2.extensions.set_wait_callback(
|
||||||
|
lambda conn: psycopg2.extras.wait_select(stub))
|
||||||
|
return stub
|
||||||
|
|
||||||
|
def test_flush_on_write(self):
|
||||||
|
# a very large query requires a flush loop to be sent to the backend
|
||||||
|
conn = self.connect()
|
||||||
|
stub = self.set_stub_wait_callback(conn)
|
||||||
|
curs = conn.cursor()
|
||||||
|
for mb in 1, 5, 10, 20, 50:
|
||||||
|
size = mb * 1024 * 1024
|
||||||
|
del stub.polls[:]
|
||||||
|
curs.execute("select %s;", ('x' * size,))
|
||||||
|
self.assertEqual(size, len(curs.fetchone()[0]))
|
||||||
|
if stub.polls.count(psycopg2.extensions.POLL_WRITE) > 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.fail("sending a large query didn't trigger block on write.")
|
||||||
|
|
||||||
|
|
||||||
|
def test_suite():
|
||||||
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user