mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-29 12:23:42 +03:00
async fixes and better connection/cursor management.
This commit is contained in:
parent
61b4ff6e6f
commit
1b74bb897f
26
ChangeLog
26
ChangeLog
|
@ -1,3 +1,27 @@
|
||||||
|
2004-12-10 Federico Di Gregorio <fog@debian.org>
|
||||||
|
|
||||||
|
* psycopg/cursor_type.c: now *all* write or async accesses to the
|
||||||
|
connection object are arbitrated using the connection lock.
|
||||||
|
|
||||||
|
* psycopg/cursor_type.c (psyco_curs_isready): now we reset the
|
||||||
|
current async cursor if it is ready, to allow other cursors to
|
||||||
|
.execute() without raising the "transaction in progress" error.
|
||||||
|
|
||||||
|
* psycopg/pqpath.c (pq_is_busy): gained status of high-level
|
||||||
|
function with its own blocking and locking.
|
||||||
|
|
||||||
|
* psycopg/cursor.h (EXC_IF_CURS_CLOSED): also checks the
|
||||||
|
connection (a closed connection implies a closed cursor.)
|
||||||
|
|
||||||
|
* psycopg/cursor_type.c: cursor's connection is correctly
|
||||||
|
INCREFfed and DECREFfed.
|
||||||
|
|
||||||
|
* psycopg/connection_type.c: removed the cursors list from the
|
||||||
|
connection object. It is not necessary anymore for the connection
|
||||||
|
to know about the cursors and the reference counting will keep the
|
||||||
|
connection alive (but possibly closed) until all cursors are
|
||||||
|
garbage collected.
|
||||||
|
|
||||||
2004-11-20 Federico Di Gregorio <fog@initd.org>
|
2004-11-20 Federico Di Gregorio <fog@initd.org>
|
||||||
|
|
||||||
* psycopg/cursor_type.c (_mogrify): ported %% fix from 1.1.15.
|
* psycopg/cursor_type.c (_mogrify): ported %% fix from 1.1.15.
|
||||||
|
@ -19,7 +43,7 @@
|
||||||
tuples are filled using PyTuple_SET_ITEM while extended types
|
tuples are filled using PyTuple_SET_ITEM while extended types
|
||||||
(created via row_factory) are filled using PySequence_SetItem.
|
(created via row_factory) are filled using PySequence_SetItem.
|
||||||
|
|
||||||
* psycopg/cursor_type.c: change cursor attribute name from
|
* psycopg/cursor_type.c: changed cursor attribute name from
|
||||||
tuple_factory to row_factory.
|
tuple_factory to row_factory.
|
||||||
|
|
||||||
2004-10-14 Federico Di Gregorio <fog@debian.org>
|
2004-10-14 Federico Di Gregorio <fog@debian.org>
|
||||||
|
|
11
NEWS
11
NEWS
|
@ -1,3 +1,14 @@
|
||||||
|
What's new in psycopg 1.99.11
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
* 'cursor' argument in .cursor() connection method renamed to
|
||||||
|
'cursor_factory'.
|
||||||
|
|
||||||
|
* changed 'tuple_factory' cursor attribute name to 'row_factory'.
|
||||||
|
|
||||||
|
* the .cursor attribute is gone and connections and cursors are propely
|
||||||
|
gc-managed.
|
||||||
|
|
||||||
What's new in psycopg 1.99.10
|
What's new in psycopg 1.99.10
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,6 @@ extern PyTypeObject connectionType;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject HEAD;
|
PyObject HEAD;
|
||||||
|
|
||||||
PyObject *cursors; /* all cursors derived from this connection */
|
|
||||||
|
|
||||||
pthread_mutex_t lock; /* the global connection lock */
|
pthread_mutex_t lock; /* the global connection lock */
|
||||||
|
|
||||||
char *dsn; /* data source name */
|
char *dsn; /* data source name */
|
||||||
|
|
|
@ -131,9 +131,6 @@ conn_connect(connectionObject *self)
|
||||||
void
|
void
|
||||||
conn_close(connectionObject *self)
|
conn_close(connectionObject *self)
|
||||||
{
|
{
|
||||||
int len, i;
|
|
||||||
PyObject *t = NULL;
|
|
||||||
|
|
||||||
/* sets this connection as closed even for other threads; also note that
|
/* sets this connection as closed even for other threads; also note that
|
||||||
we need to check the value of pgconn, because we get called even when
|
we need to check the value of pgconn, because we get called even when
|
||||||
the connection fails! */
|
the connection fails! */
|
||||||
|
@ -144,33 +141,16 @@ conn_close(connectionObject *self)
|
||||||
|
|
||||||
/* execute a forced rollback on the connection (but don't check the
|
/* execute a forced rollback on the connection (but don't check the
|
||||||
result, we're going to close the pq connection anyway */
|
result, we're going to close the pq connection anyway */
|
||||||
if (self->pgconn) pq_abort(self);
|
|
||||||
|
|
||||||
/* orphans all the children cursors but do NOT destroy them (note that we
|
|
||||||
need to lock the connection before orphaning a cursor: we don't want to
|
|
||||||
remove a connection from a cursor executing a DB operation */
|
|
||||||
pthread_mutex_unlock(&self->lock);
|
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&self->lock);
|
|
||||||
len = PyList_Size(self->cursors);
|
|
||||||
Dprintf("conn_close: ophaning %d cursors", len);
|
|
||||||
for (i = len-1; i >= 0; i--) {
|
|
||||||
t = PySequence_GetItem(self->cursors, i);
|
|
||||||
Dprintf("conn close: cursor at %p: refcnt = %d", t, t->ob_refcnt);
|
|
||||||
PySequence_DelItem(self->cursors, i);
|
|
||||||
((cursorObject *)t)->conn = NULL; /* orphaned */
|
|
||||||
Dprintf("conn_close: -> new refcnt = %d", t->ob_refcnt);
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&self->lock);
|
|
||||||
|
|
||||||
/* now that all cursors have been orphaned (they can't operate on the
|
|
||||||
database anymore) we can shut down the connection */
|
|
||||||
if (self->pgconn) {
|
if (self->pgconn) {
|
||||||
|
pq_abort(self);
|
||||||
PQfinish(self->pgconn);
|
PQfinish(self->pgconn);
|
||||||
Dprintf("conn_close: PQfinish called");
|
Dprintf("conn_close: PQfinish called");
|
||||||
self->pgconn = NULL;
|
self->pgconn = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&self->lock);
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* conn_commit - commit on a connection */
|
/* conn_commit - commit on a connection */
|
||||||
|
|
|
@ -65,11 +65,6 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
|
||||||
|
|
||||||
/* TODO: added error checking on obj (cursor) here */
|
/* TODO: added error checking on obj (cursor) here */
|
||||||
|
|
||||||
/* add the cursor to this connection's list (and decref it, so that it has
|
|
||||||
the right number of references to go away even if still in the list) */
|
|
||||||
PyList_Append(self->cursors, obj);
|
|
||||||
Py_DECREF(obj);
|
|
||||||
|
|
||||||
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = %d",
|
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = %d",
|
||||||
obj, obj->ob_refcnt);
|
obj, obj->ob_refcnt);
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -244,7 +239,6 @@ static struct PyMemberDef connectionObject_members[] = {
|
||||||
{"isolation_level", T_LONG,
|
{"isolation_level", T_LONG,
|
||||||
offsetof(connectionObject, isolation_level), RO},
|
offsetof(connectionObject, isolation_level), RO},
|
||||||
{"encoding", T_STRING, offsetof(connectionObject, encoding), RO},
|
{"encoding", T_STRING, offsetof(connectionObject, encoding), RO},
|
||||||
{"cursors", T_OBJECT, offsetof(connectionObject, cursors), RO},
|
|
||||||
{"notices", T_OBJECT, offsetof(connectionObject, notice_list), RO},
|
{"notices", T_OBJECT, offsetof(connectionObject, notice_list), RO},
|
||||||
{"notifies", T_OBJECT, offsetof(connectionObject, notifies), RO},
|
{"notifies", T_OBJECT, offsetof(connectionObject, notifies), RO},
|
||||||
{"dsn", T_STRING, offsetof(connectionObject, dsn), RO},
|
{"dsn", T_STRING, offsetof(connectionObject, dsn), RO},
|
||||||
|
@ -261,7 +255,6 @@ connection_setup(connectionObject *self, char *dsn)
|
||||||
self, ((PyObject *)self)->ob_refcnt);
|
self, ((PyObject *)self)->ob_refcnt);
|
||||||
|
|
||||||
self->dsn = strdup(dsn);
|
self->dsn = strdup(dsn);
|
||||||
self->cursors = PyList_New(0);
|
|
||||||
self->notice_list = PyList_New(0);
|
self->notice_list = PyList_New(0);
|
||||||
self->closed = 0;
|
self->closed = 0;
|
||||||
self->isolation_level = 1;
|
self->isolation_level = 1;
|
||||||
|
@ -273,7 +266,6 @@ connection_setup(connectionObject *self, char *dsn)
|
||||||
pthread_mutex_init(&(self->lock), NULL);
|
pthread_mutex_init(&(self->lock), NULL);
|
||||||
|
|
||||||
if (conn_connect(self) != 0) {
|
if (conn_connect(self) != 0) {
|
||||||
Py_XDECREF(self->cursors);
|
|
||||||
pthread_mutex_destroy(&(self->lock));
|
pthread_mutex_destroy(&(self->lock));
|
||||||
Dprintf("connection_init: FAILED");
|
Dprintf("connection_init: FAILED");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -291,7 +283,6 @@ connection_dealloc(PyObject* obj)
|
||||||
|
|
||||||
if (self->closed == 0) conn_close(self);
|
if (self->closed == 0) conn_close(self);
|
||||||
|
|
||||||
Py_XDECREF(self->cursors);
|
|
||||||
if (self->dsn) free(self->dsn);
|
if (self->dsn) free(self->dsn);
|
||||||
if (self->encoding) free(self->encoding);
|
if (self->encoding) free(self->encoding);
|
||||||
if (self->critical) free(self->critical);
|
if (self->critical) free(self->critical);
|
||||||
|
|
|
@ -72,7 +72,8 @@ typedef struct {
|
||||||
extern void curs_reset(cursorObject *self);
|
extern void curs_reset(cursorObject *self);
|
||||||
|
|
||||||
/* exception-raising macros */
|
/* exception-raising macros */
|
||||||
#define EXC_IF_CURS_CLOSED(self) if ((self)->closed) { \
|
#define EXC_IF_CURS_CLOSED(self) \
|
||||||
|
if ((self)->closed || ((self)->conn && (self)->conn->closed)) { \
|
||||||
PyErr_SetString(InterfaceError, "cursor already closed"); \
|
PyErr_SetString(InterfaceError, "cursor already closed"); \
|
||||||
return NULL; }
|
return NULL; }
|
||||||
|
|
||||||
|
|
|
@ -249,12 +249,15 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
if (self->conn->async_cursor != NULL
|
if (self->conn->async_cursor != NULL
|
||||||
&& self->conn->async_cursor != (PyObject*)self) {
|
&& self->conn->async_cursor != (PyObject*)self) {
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
PyErr_SetString(ProgrammingError,
|
PyErr_SetString(ProgrammingError,
|
||||||
"asynchronous query already in execution");
|
"asynchronous query already in execution");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
|
||||||
if (PyUnicode_Check(operation)) {
|
if (PyUnicode_Check(operation)) {
|
||||||
PyObject *enc = PyDict_GetItemString(psycoEncodings,
|
PyObject *enc = PyDict_GetItemString(psycoEncodings,
|
||||||
|
@ -498,12 +501,15 @@ _psyco_curs_prefetch(cursorObject *self)
|
||||||
|
|
||||||
/* check if the fetching cursor is the one that did the asynchronous query
|
/* check if the fetching cursor is the one that did the asynchronous query
|
||||||
and raise an exception if not */
|
and raise an exception if not */
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
if (self->conn->async_cursor != NULL
|
if (self->conn->async_cursor != NULL
|
||||||
&& self->conn->async_cursor != (PyObject*)self) {
|
&& self->conn->async_cursor != (PyObject*)self) {
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
PyErr_SetString(ProgrammingError,
|
PyErr_SetString(ProgrammingError,
|
||||||
"asynchronous fetch by wrong cursor");
|
"asynchronous fetch by wrong cursor");
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
|
||||||
if (self->pgres == NULL) {
|
if (self->pgres == NULL) {
|
||||||
Dprintf("_psyco_curs_prefetch: trying to fetch data");
|
Dprintf("_psyco_curs_prefetch: trying to fetch data");
|
||||||
|
@ -622,7 +628,6 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
|
||||||
/* if the query was async aggresively free pgres, to allow
|
/* if the query was async aggresively free pgres, to allow
|
||||||
successive requests to reallocate it */
|
successive requests to reallocate it */
|
||||||
if (self->row >= self->rowcount
|
if (self->row >= self->rowcount
|
||||||
|
|
||||||
&& self->conn->async_cursor == (PyObject*)self)
|
&& self->conn->async_cursor == (PyObject*)self)
|
||||||
IFCLEARPGRES(self->pgres);
|
IFCLEARPGRES(self->pgres);
|
||||||
|
|
||||||
|
@ -687,7 +692,8 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
|
||||||
|
|
||||||
/* if the query was async aggresively free pgres, to allow
|
/* if the query was async aggresively free pgres, to allow
|
||||||
successive requests to reallocate it */
|
successive requests to reallocate it */
|
||||||
if (self->row >= self->rowcount && self->conn->async_cursor)
|
if (self->row >= self->rowcount
|
||||||
|
&& self->conn->async_cursor == (PyObject*)self)
|
||||||
IFCLEARPGRES(self->pgres);
|
IFCLEARPGRES(self->pgres);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
@ -743,7 +749,8 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
|
||||||
|
|
||||||
/* if the query was async aggresively free pgres, to allow
|
/* if the query was async aggresively free pgres, to allow
|
||||||
successive requests to reallocate it */
|
successive requests to reallocate it */
|
||||||
if (self->row >= self->rowcount && self->conn->async_cursor)
|
if (self->row >= self->rowcount
|
||||||
|
&& self->conn->async_cursor == (PyObject*)self)
|
||||||
IFCLEARPGRES(self->pgres);
|
IFCLEARPGRES(self->pgres);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
@ -929,13 +936,21 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
psyco_curs_fileno(cursorObject *self, PyObject *args)
|
psyco_curs_fileno(cursorObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
|
long int socket;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "")) return NULL;
|
if (!PyArg_ParseTuple(args, "")) return NULL;
|
||||||
EXC_IF_CURS_CLOSED(self);
|
EXC_IF_CURS_CLOSED(self);
|
||||||
|
|
||||||
/* note how we call PQflush() to make sure the user will use
|
/* note how we call PQflush() to make sure the user will use
|
||||||
select() in the safe way! */
|
select() in the safe way! */
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
PQflush(self->conn->pgconn);
|
PQflush(self->conn->pgconn);
|
||||||
return PyInt_FromLong((long int)PQsocket(self->conn->pgconn));
|
socket = (long int)PQsocket(self->conn->pgconn);
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
|
||||||
|
return PyInt_FromLong(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extension: isready - return true if data from async execute is ready */
|
/* extension: isready - return true if data from async execute is ready */
|
||||||
|
@ -949,11 +964,20 @@ psyco_curs_isready(cursorObject *self, PyObject *args)
|
||||||
if (!PyArg_ParseTuple(args, "")) return NULL;
|
if (!PyArg_ParseTuple(args, "")) return NULL;
|
||||||
EXC_IF_CURS_CLOSED(self);
|
EXC_IF_CURS_CLOSED(self);
|
||||||
|
|
||||||
|
/* pq_is_busy does its own locking, we don't need anything special but if
|
||||||
|
the cursor is ready we need to fetch the result and free the connection
|
||||||
|
for the next query. */
|
||||||
|
|
||||||
if (pq_is_busy(self->conn)) {
|
if (pq_is_busy(self->conn)) {
|
||||||
Py_INCREF(Py_False);
|
Py_INCREF(Py_False);
|
||||||
return Py_False;
|
return Py_False;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
IFCLEARPGRES(self->pgres);
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
self->pgres = PQgetResult(self->conn->pgconn);
|
||||||
|
self->conn->async_cursor = NULL;
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
Py_INCREF(Py_True);
|
Py_INCREF(Py_True);
|
||||||
return Py_True;
|
return Py_True;
|
||||||
}
|
}
|
||||||
|
@ -1061,6 +1085,8 @@ cursor_setup(cursorObject *self, connectionObject *conn)
|
||||||
self, ((PyObject *)self)->ob_refcnt);
|
self, ((PyObject *)self)->ob_refcnt);
|
||||||
|
|
||||||
self->conn = conn;
|
self->conn = conn;
|
||||||
|
Py_INCREF((PyObject*)self->conn);
|
||||||
|
|
||||||
self->closed = 0;
|
self->closed = 0;
|
||||||
|
|
||||||
self->pgres = NULL;
|
self->pgres = NULL;
|
||||||
|
@ -1092,25 +1118,10 @@ cursor_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
cursorObject *self = (cursorObject *)obj;
|
cursorObject *self = (cursorObject *)obj;
|
||||||
|
|
||||||
/* if necessary remove cursor from connection */
|
|
||||||
if (self->conn != NULL) {
|
|
||||||
PyObject *t;
|
|
||||||
int len, i;
|
|
||||||
|
|
||||||
if ((len = PyList_Size(self->conn->cursors)) > 0) {
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
t = PyList_GET_ITEM(self->conn->cursors, i);
|
|
||||||
if (self == (cursorObject *)t) {
|
|
||||||
Dprintf("cursor_dealloc: found myself in cursor list");
|
|
||||||
PySequence_DelItem(self->conn->cursors, i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->query) free(self->query);
|
if (self->query) free(self->query);
|
||||||
|
|
||||||
|
Py_DECREF((PyObject*)self->conn);
|
||||||
Py_XDECREF(self->casts);
|
Py_XDECREF(self->casts);
|
||||||
Py_XDECREF(self->description);
|
Py_XDECREF(self->description);
|
||||||
Py_XDECREF(self->pgstatus);
|
Py_XDECREF(self->pgstatus);
|
||||||
|
|
|
@ -19,6 +19,12 @@
|
||||||
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* IMPORTANT NOTE: no function in this file do its own connection locking
|
||||||
|
except for pg_execute and pq_fetch (that are somehow high-level. This means
|
||||||
|
that all the othe functions should be called while holding a lock to the
|
||||||
|
connection.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -33,7 +39,9 @@
|
||||||
#include "psycopg/pgtypes.h"
|
#include "psycopg/pgtypes.h"
|
||||||
#include "psycopg/pgversion.h"
|
#include "psycopg/pgversion.h"
|
||||||
|
|
||||||
/* pq_raise - raise a python exception of the right kind */
|
/* pq_raise - raise a python exception of the right kind
|
||||||
|
|
||||||
|
This function should be called while holding the GIL. */
|
||||||
|
|
||||||
void
|
void
|
||||||
pq_raise(connectionObject *conn, cursorObject *curs, PyObject *exc, char *msg)
|
pq_raise(connectionObject *conn, cursorObject *curs, PyObject *exc, char *msg)
|
||||||
|
@ -106,7 +114,8 @@ pq_raise(connectionObject *conn, cursorObject *curs, PyObject *exc, char *msg)
|
||||||
critical condition like out of memory or lost connection. it save the error
|
critical condition like out of memory or lost connection. it save the error
|
||||||
message and mark the connection as 'wanting cleanup'.
|
message and mark the connection as 'wanting cleanup'.
|
||||||
|
|
||||||
both functions do not call any Py_*_ALLOW_THREADS macros. */
|
both functions do not call any Py_*_ALLOW_THREADS macros.
|
||||||
|
pq_resolve_critical should be called while holding the GIL. */
|
||||||
|
|
||||||
void
|
void
|
||||||
pq_set_critical(connectionObject *conn, const char *msg)
|
pq_set_critical(connectionObject *conn, const char *msg)
|
||||||
|
@ -140,6 +149,7 @@ pq_resolve_critical(connectionObject *conn, int close)
|
||||||
note that this function does block because it needs to wait for the full
|
note that this function does block because it needs to wait for the full
|
||||||
result sets of the previous query to clear them.
|
result sets of the previous query to clear them.
|
||||||
|
|
||||||
|
|
||||||
this function does not call any Py_*_ALLOW_THREADS macros */
|
this function does not call any Py_*_ALLOW_THREADS macros */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -149,6 +159,7 @@ pq_clear_async(connectionObject *conn)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
pgres = PQgetResult(conn->pgconn);
|
pgres = PQgetResult(conn->pgconn);
|
||||||
|
Dprintf("pq_clear_async: clearing PGresult at %p", pgres);
|
||||||
IFCLEARPGRES(pgres);
|
IFCLEARPGRES(pgres);
|
||||||
} while (pgres != NULL);
|
} while (pgres != NULL);
|
||||||
}
|
}
|
||||||
|
@ -291,7 +302,10 @@ pq_abort(connectionObject *conn)
|
||||||
|
|
||||||
a status of 1 means that a call to pq_fetch will block, while a status of 0
|
a status of 1 means that a call to pq_fetch will block, while a status of 0
|
||||||
means that there is data available to be collected. -1 means an error, the
|
means that there is data available to be collected. -1 means an error, the
|
||||||
exception will be set accordingly. */
|
exception will be set accordingly.
|
||||||
|
|
||||||
|
this fucntion locks the connection object
|
||||||
|
this function call Py_*_ALLOW_THREADS macros */
|
||||||
|
|
||||||
int
|
int
|
||||||
pq_is_busy(connectionObject *conn)
|
pq_is_busy(connectionObject *conn)
|
||||||
|
@ -299,9 +313,15 @@ pq_is_busy(connectionObject *conn)
|
||||||
PGnotify *pgn;
|
PGnotify *pgn;
|
||||||
|
|
||||||
Dprintf("pq_is_busy: consuming input");
|
Dprintf("pq_is_busy: consuming input");
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(conn->lock));
|
||||||
|
|
||||||
if (PQconsumeInput(conn->pgconn) == 0) {
|
if (PQconsumeInput(conn->pgconn) == 0) {
|
||||||
Dprintf("pq_is_busy: PQconsumeInput() failed");
|
Dprintf("pq_is_busy: PQconsumeInput() failed");
|
||||||
PyErr_SetString(OperationalError, PQerrorMessage(conn->pgconn));
|
PyErr_SetString(OperationalError, PQerrorMessage(conn->pgconn));
|
||||||
|
pthread_mutex_unlock(&(conn->lock));
|
||||||
|
Py_BLOCK_THREADS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,12 +335,13 @@ pq_is_busy(connectionObject *conn)
|
||||||
notify = PyTuple_New(2);
|
notify = PyTuple_New(2);
|
||||||
PyTuple_SET_ITEM(notify, 0, PyInt_FromLong((long)pgn->be_pid));
|
PyTuple_SET_ITEM(notify, 0, PyInt_FromLong((long)pgn->be_pid));
|
||||||
PyTuple_SET_ITEM(notify, 1, PyString_FromString(pgn->relname));
|
PyTuple_SET_ITEM(notify, 1, PyString_FromString(pgn->relname));
|
||||||
pthread_mutex_lock(&(conn->lock));
|
|
||||||
PyList_Append(conn->notifies, notify);
|
PyList_Append(conn->notifies, notify);
|
||||||
pthread_mutex_unlock(&(conn->lock));
|
|
||||||
free(pgn);
|
free(pgn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
return PQisBusy(conn->pgconn);
|
return PQisBusy(conn->pgconn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,14 +677,14 @@ pq_fetch(cursorObject *curs)
|
||||||
|
|
||||||
Dprintf("pq_fetch: no data: entering polling loop");
|
Dprintf("pq_fetch: no data: entering polling loop");
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
pthread_mutex_lock(&(curs->conn->lock));
|
|
||||||
|
|
||||||
while (pq_is_busy(curs->conn) > 0) {
|
while (pq_is_busy(curs->conn) > 0) {
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
int sval, sock;
|
int sval, sock;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(curs->conn->lock));
|
||||||
|
|
||||||
sock = PQsocket(curs->conn->pgconn);
|
sock = PQsocket(curs->conn->pgconn);
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(sock, &rfds);
|
FD_SET(sock, &rfds);
|
||||||
|
@ -677,14 +698,14 @@ pq_fetch(cursorObject *curs)
|
||||||
Dprintf("pq_fetch: entering PDflush() loop");
|
Dprintf("pq_fetch: entering PDflush() loop");
|
||||||
while (PQflush(curs->conn->pgconn) != 0);
|
while (PQflush(curs->conn->pgconn) != 0);
|
||||||
sval = select(sock+1, &rfds, NULL, NULL, &tv);
|
sval = select(sock+1, &rfds, NULL, NULL, &tv);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(curs->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dprintf("pq_fetch: data is probably ready");
|
Dprintf("pq_fetch: data is probably ready");
|
||||||
IFCLEARPGRES(curs->pgres);
|
IFCLEARPGRES(curs->pgres);
|
||||||
curs->pgres = PQgetResult(curs->conn->pgconn);
|
curs->pgres = PQgetResult(curs->conn->pgconn);
|
||||||
|
|
||||||
pthread_mutex_unlock(&(curs->conn->lock));
|
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for PGRES_FATAL_ERROR result */
|
/* check for PGRES_FATAL_ERROR result */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user