mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-01-31 17:34:08 +03:00
poll implementation for async, sync and green connection unified.
This commit is contained in:
parent
0da4befe78
commit
cb40342afa
|
@ -45,16 +45,6 @@ extern "C" {
|
||||||
#define CONN_STATUS_DATESTYLE 21
|
#define CONN_STATUS_DATESTYLE 21
|
||||||
#define CONN_STATUS_CLIENT_ENCODING 22
|
#define CONN_STATUS_CLIENT_ENCODING 22
|
||||||
|
|
||||||
/* TODO: REMOVE THOSE */
|
|
||||||
#define CONN_STATUS_SYNC 3
|
|
||||||
#define CONN_STATUS_ASYNC 4
|
|
||||||
#define CONN_STATUS_SEND_DATESTYLE 5
|
|
||||||
#define CONN_STATUS_SENT_DATESTYLE 6
|
|
||||||
#define CONN_STATUS_GET_DATESTYLE 7
|
|
||||||
#define CONN_STATUS_SEND_CLIENT_ENCODING 8
|
|
||||||
#define CONN_STATUS_SENT_CLIENT_ENCODING 9
|
|
||||||
#define CONN_STATUS_GET_CLIENT_ENCODING 10
|
|
||||||
|
|
||||||
/* async query execution status */
|
/* async query execution status */
|
||||||
#define ASYNC_DONE 0
|
#define ASYNC_DONE 0
|
||||||
#define ASYNC_READ 1
|
#define ASYNC_READ 1
|
||||||
|
@ -137,12 +127,7 @@ HIDDEN int conn_commit(connectionObject *self);
|
||||||
HIDDEN int conn_rollback(connectionObject *self);
|
HIDDEN int conn_rollback(connectionObject *self);
|
||||||
HIDDEN int conn_switch_isolation_level(connectionObject *self, int level);
|
HIDDEN int conn_switch_isolation_level(connectionObject *self, int level);
|
||||||
HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc);
|
HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc);
|
||||||
HIDDEN PyObject *conn_poll_connect_send(connectionObject *self);
|
HIDDEN int conn_poll(connectionObject *self);
|
||||||
HIDDEN PyObject *conn_poll_connect_fetch(connectionObject *self);
|
|
||||||
HIDDEN PyObject *conn_poll_ready(connectionObject *self);
|
|
||||||
HIDDEN PyObject *conn_poll_send(connectionObject *self);
|
|
||||||
HIDDEN PyObject *conn_poll_fetch(connectionObject *self);
|
|
||||||
HIDDEN PyObject *conn_poll_green(connectionObject *self);
|
|
||||||
|
|
||||||
/* exception-raising macros */
|
/* exception-raising macros */
|
||||||
#define EXC_IF_CONN_CLOSED(self) if ((self)->closed > 0) { \
|
#define EXC_IF_CONN_CLOSED(self) if ((self)->closed > 0) { \
|
||||||
|
|
|
@ -208,7 +208,6 @@ conn_get_encoding(PGresult *pgres)
|
||||||
for (i=0 ; i < strlen(tmp) ; i++)
|
for (i=0 ; i < strlen(tmp) ; i++)
|
||||||
encoding[i] = toupper(tmp[i]);
|
encoding[i] = toupper(tmp[i]);
|
||||||
encoding[i] = '\0';
|
encoding[i] = '\0';
|
||||||
CLEARPGRES(pgres);
|
|
||||||
|
|
||||||
return encoding;
|
return encoding;
|
||||||
}
|
}
|
||||||
|
@ -258,10 +257,18 @@ conn_setup(connectionObject *self, PGconn *pgconn)
|
||||||
{
|
{
|
||||||
PGresult *pgres;
|
PGresult *pgres;
|
||||||
|
|
||||||
|
self->equote = conn_get_standard_conforming_strings(pgconn);
|
||||||
|
self->server_version = conn_get_server_version(pgconn);
|
||||||
|
self->protocol = conn_get_protocol_version(self->pgconn);
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
pthread_mutex_lock(&self->lock);
|
pthread_mutex_lock(&self->lock);
|
||||||
Py_BLOCK_THREADS;
|
Py_BLOCK_THREADS;
|
||||||
|
|
||||||
|
if (pq_set_non_blocking(self, 1, 1) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!psyco_green()) {
|
if (!psyco_green()) {
|
||||||
Py_UNBLOCK_THREADS;
|
Py_UNBLOCK_THREADS;
|
||||||
pgres = PQexec(pgconn, psyco_datestyle);
|
pgres = PQexec(pgconn, psyco_datestyle);
|
||||||
|
@ -305,6 +312,7 @@ conn_setup(connectionObject *self, PGconn *pgconn)
|
||||||
Py_BLOCK_THREADS;
|
Py_BLOCK_THREADS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
CLEARPGRES(pgres);
|
||||||
|
|
||||||
if (!psyco_green()) {
|
if (!psyco_green()) {
|
||||||
Py_UNBLOCK_THREADS;
|
Py_UNBLOCK_THREADS;
|
||||||
|
@ -334,8 +342,8 @@ conn_setup(connectionObject *self, PGconn *pgconn)
|
||||||
|
|
||||||
/* conn_connect - execute a connection to the database */
|
/* conn_connect - execute a connection to the database */
|
||||||
|
|
||||||
int
|
static int
|
||||||
conn_sync_connect(connectionObject *self)
|
_conn_sync_connect(connectionObject *self)
|
||||||
{
|
{
|
||||||
PGconn *pgconn;
|
PGconn *pgconn;
|
||||||
PyObject *wait_rv;
|
PyObject *wait_rv;
|
||||||
|
@ -382,19 +390,11 @@ conn_sync_connect(connectionObject *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->equote = conn_get_standard_conforming_strings(pgconn);
|
|
||||||
self->server_version = conn_get_server_version(pgconn);
|
|
||||||
self->protocol = conn_get_protocol_version(self->pgconn);
|
|
||||||
|
|
||||||
/* From here the connection is considered ready: with the new status,
|
/* From here the connection is considered ready: with the new status,
|
||||||
* poll() will use PQisBusy instead of PQconnectPoll.
|
* poll() will use PQisBusy instead of PQconnectPoll.
|
||||||
*/
|
*/
|
||||||
self->status = CONN_STATUS_READY;
|
self->status = CONN_STATUS_READY;
|
||||||
|
|
||||||
if (pq_set_non_blocking(self, 1, 1) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn_setup(self, self->pgconn) == -1) {
|
if (conn_setup(self, self->pgconn) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -403,7 +403,7 @@ conn_sync_connect(connectionObject *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
conn_async_connect(connectionObject *self)
|
_conn_async_connect(connectionObject *self)
|
||||||
{
|
{
|
||||||
PGconn *pgconn;
|
PGconn *pgconn;
|
||||||
|
|
||||||
|
@ -426,7 +426,10 @@ conn_async_connect(connectionObject *self)
|
||||||
|
|
||||||
PQsetNoticeProcessor(pgconn, conn_notice_callback, (void*)self);
|
PQsetNoticeProcessor(pgconn, conn_notice_callback, (void*)self);
|
||||||
|
|
||||||
self->status = CONN_STATUS_SETUP;
|
/* The connection will be completed banging on poll():
|
||||||
|
* First with _conn_poll_connecting() that will finish connection,
|
||||||
|
* then with _conn_poll_setup_async() that will do the same job
|
||||||
|
* of setup_async(). */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -436,340 +439,23 @@ conn_connect(connectionObject *self, long int async)
|
||||||
{
|
{
|
||||||
if (async == 1) {
|
if (async == 1) {
|
||||||
Dprintf("con_connect: connecting in ASYNC mode");
|
Dprintf("con_connect: connecting in ASYNC mode");
|
||||||
return conn_async_connect(self);
|
return _conn_async_connect(self);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Dprintf("con_connect: connecting in SYNC mode");
|
Dprintf("con_connect: connecting in SYNC mode");
|
||||||
return conn_sync_connect(self);
|
return _conn_sync_connect(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* conn_poll_connect_send - handle connection polling when flushing output
|
|
||||||
during asynchronous connection attempt. */
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
conn_poll_connect_send(connectionObject *self)
|
|
||||||
{
|
|
||||||
const char *query = NULL;
|
|
||||||
int next_status;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
Dprintf("conn_poll_connect_send: status %d", self->status);
|
|
||||||
|
|
||||||
switch (self->status) {
|
|
||||||
case CONN_STATUS_SEND_DATESTYLE:
|
|
||||||
/* set the datestyle */
|
|
||||||
query = psyco_datestyle;
|
|
||||||
next_status = CONN_STATUS_SENT_DATESTYLE;
|
|
||||||
break;
|
|
||||||
case CONN_STATUS_SEND_CLIENT_ENCODING:
|
|
||||||
/* get the client_encoding */
|
|
||||||
query = psyco_client_encoding;
|
|
||||||
next_status = CONN_STATUS_SENT_CLIENT_ENCODING;
|
|
||||||
break;
|
|
||||||
case CONN_STATUS_SENT_DATESTYLE:
|
|
||||||
case CONN_STATUS_SENT_CLIENT_ENCODING:
|
|
||||||
/* the query has only been partially sent */
|
|
||||||
query = NULL;
|
|
||||||
next_status = self->status;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* unexpected state, error out */
|
|
||||||
PyErr_Format(OperationalError,
|
|
||||||
"unexpected state: %d", self->status);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dprintf("conn_poll_connect_send: sending query %-.200s", query);
|
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
pthread_mutex_lock(&(self->lock));
|
|
||||||
|
|
||||||
if (query != NULL) {
|
|
||||||
if (PQsendQuery(self->pgconn, query) != 1) {
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_BLOCK_THREADS;
|
|
||||||
PyErr_SetString(OperationalError,
|
|
||||||
PQerrorMessage(self->pgconn));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PQflush(self->pgconn) == 0) {
|
|
||||||
/* the query got fully sent to the server */
|
|
||||||
Dprintf("conn_poll_connect_send: query got flushed immediately");
|
|
||||||
/* the return value will be POLL_READ */
|
|
||||||
ret = PSYCO_POLL_READ;
|
|
||||||
|
|
||||||
/* advance the next status, since we skip over the "waiting for the
|
|
||||||
query to be sent" status */
|
|
||||||
switch (next_status) {
|
|
||||||
case CONN_STATUS_SENT_DATESTYLE:
|
|
||||||
next_status = CONN_STATUS_GET_DATESTYLE;
|
|
||||||
break;
|
|
||||||
case CONN_STATUS_SENT_CLIENT_ENCODING:
|
|
||||||
next_status = CONN_STATUS_GET_CLIENT_ENCODING;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* query did not get sent completely, tell the client to wait for the
|
|
||||||
socket to become writable */
|
|
||||||
ret = PSYCO_POLL_WRITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->status = next_status;
|
|
||||||
Dprintf("conn_poll_connect_send: next status is %d, returning %d",
|
|
||||||
self->status, ret);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
|
|
||||||
return PyInt_FromLong(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* conn_poll_connect_fetch - handle connection polling when reading result
|
|
||||||
during asynchronous connection attempt. */
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
conn_poll_connect_fetch(connectionObject *self)
|
|
||||||
{
|
|
||||||
PGresult *pgres;
|
|
||||||
int is_busy;
|
|
||||||
int next_status;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
Dprintf("conn_poll_connect_fetch: status %d", self->status);
|
|
||||||
|
|
||||||
/* consume the input */
|
|
||||||
is_busy = pq_is_busy(self);
|
|
||||||
if (is_busy == -1) {
|
|
||||||
/* there was an error, raise the exception */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else if (is_busy == 1) {
|
|
||||||
/* the connection is busy, tell the user to wait more */
|
|
||||||
Dprintf("conn_poll_connect_fetch: connection busy, returning %d",
|
|
||||||
PSYCO_POLL_READ);
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
pthread_mutex_lock(&(self->lock));
|
|
||||||
|
|
||||||
/* connection no longer busy, process input */
|
|
||||||
pgres = PQgetResult(self->pgconn);
|
|
||||||
|
|
||||||
/* do the rest while holding the GIL, we won't be calling into any
|
|
||||||
blocking API */
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
|
|
||||||
Dprintf("conn_poll_connect_fetch: got result %p", pgres);
|
|
||||||
|
|
||||||
/* we expect COMMAND_OK (from SET) or TUPLES_OK (from SHOW) */
|
|
||||||
if (pgres == NULL || (PQresultStatus(pgres) != PGRES_COMMAND_OK &&
|
|
||||||
PQresultStatus(pgres) != PGRES_TUPLES_OK)) {
|
|
||||||
PyErr_SetString(OperationalError, "can't issue "
|
|
||||||
"initial connection queries");
|
|
||||||
IFCLEARPGRES(pgres);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->status == CONN_STATUS_GET_DATESTYLE) {
|
|
||||||
/* got the result from SET DATESTYLE*/
|
|
||||||
Dprintf("conn_poll_connect_fetch: datestyle set");
|
|
||||||
next_status = CONN_STATUS_SEND_CLIENT_ENCODING;
|
|
||||||
}
|
|
||||||
else if (self->status == CONN_STATUS_GET_CLIENT_ENCODING) {
|
|
||||||
/* got the client_encoding */
|
|
||||||
self->encoding = conn_get_encoding(pgres);
|
|
||||||
if (self->encoding == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
Dprintf("conn_poll_connect_fetch: got client_encoding %s", self->encoding);
|
|
||||||
|
|
||||||
/* since this is the last step, set the other instance variables now */
|
|
||||||
self->equote = conn_get_standard_conforming_strings(self->pgconn);
|
|
||||||
self->protocol = conn_get_protocol_version(self->pgconn);
|
|
||||||
self->server_version = conn_get_server_version(self->pgconn);
|
|
||||||
/*
|
|
||||||
* asynchronous connections always use isolation level 0, the user is
|
|
||||||
* expected to manage the transactions himself, by sending
|
|
||||||
* (asynchronously) BEGIN and COMMIT statements.
|
|
||||||
*/
|
|
||||||
self->isolation_level = 0;
|
|
||||||
|
|
||||||
/* FIXME: this is a bug: the above queries were sent to the server
|
|
||||||
with a blocking connection */
|
|
||||||
if (pq_set_non_blocking(self, 1, 1) != 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* next status is going to READY */
|
|
||||||
next_status = CONN_STATUS_READY;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* unexpected state, error out */
|
|
||||||
PyErr_Format(OperationalError,
|
|
||||||
"unexpected state: %d", self->status);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear any leftover result, there should be none, but the protocol
|
|
||||||
requires calling PQgetResult until you get a NULL */
|
|
||||||
pq_clear_async(self);
|
|
||||||
|
|
||||||
self->status = next_status;
|
|
||||||
|
|
||||||
/* if the curent status is READY it means we got the result of the
|
|
||||||
last initialization query, so we return POLL_OK, otherwise we need to
|
|
||||||
send another query, so return POLL_WRITE */
|
|
||||||
ret = self->status == CONN_STATUS_READY ? PSYCO_POLL_OK : PSYCO_POLL_WRITE;
|
|
||||||
Dprintf("conn_poll_connect_fetch: next status is %d, returning %d",
|
|
||||||
self->status, ret);
|
|
||||||
return PyInt_FromLong(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* conn_poll_ready - handle connection polling when it is already open */
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
conn_poll_ready(connectionObject *self)
|
|
||||||
{
|
|
||||||
int is_busy;
|
|
||||||
|
|
||||||
/* if there is an asynchronous query underway, poll it */
|
|
||||||
if (self->async_cursor != NULL) {
|
|
||||||
if (self->async_status == ASYNC_WRITE) {
|
|
||||||
return conn_poll_send(self);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* this gets called both for ASYNC_READ and ASYNC_DONE, because
|
|
||||||
even if the async query is complete, we still might want to
|
|
||||||
check for NOTIFYs */
|
|
||||||
return conn_poll_fetch(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* otherwise just check for NOTIFYs */
|
|
||||||
is_busy = pq_is_busy(self);
|
|
||||||
if (is_busy == -1) {
|
|
||||||
/* there was an error, raise the exception */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else if (is_busy == 1) {
|
|
||||||
/* the connection is busy, tell the user to wait more */
|
|
||||||
Dprintf("conn_poll_ready: returning %d", PSYCO_POLL_READ);
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_READ);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* connection is idle */
|
|
||||||
Dprintf("conn_poll_ready: returning %d", PSYCO_POLL_OK);
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* conn_poll_send - poll the connection when flushing data to the backend */
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
conn_poll_send(connectionObject *self)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
|
|
||||||
/* flush queued output to the server */
|
|
||||||
res = pq_flush(self);
|
|
||||||
|
|
||||||
if (res == 1) {
|
|
||||||
/* some data still waiting to be flushed */
|
|
||||||
Dprintf("conn_poll_send: returning %d", PSYCO_POLL_WRITE);
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_WRITE);
|
|
||||||
}
|
|
||||||
else if (res == 0) {
|
|
||||||
/* all data flushed, start waiting for results */
|
|
||||||
Dprintf("conn_poll_send: returning %d", PSYCO_POLL_READ);
|
|
||||||
self->async_status = ASYNC_READ;
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_READ);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* unexpected result */
|
|
||||||
PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* conn_poll_fetch - poll the connection when reading results from the backend
|
|
||||||
*
|
|
||||||
* If self_curs is available, use it to store the result of the last query.
|
|
||||||
* Also unlink it when finished.
|
|
||||||
*/
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
conn_poll_fetch(connectionObject *self)
|
|
||||||
{
|
|
||||||
int is_busy;
|
|
||||||
int last_result;
|
|
||||||
|
|
||||||
/* consume the input */
|
|
||||||
is_busy = pq_is_busy(self);
|
|
||||||
if (is_busy == -1) {
|
|
||||||
/* there was an error, raise the exception */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else if (is_busy == 1) {
|
|
||||||
/* the connection is busy, tell the user to wait more */
|
|
||||||
Dprintf("conn_poll_fetch: returning %d", PSYCO_POLL_READ);
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* try to fetch the data only if this was a poll following a read
|
|
||||||
request; else just return POLL_OK to the user: this is necessary
|
|
||||||
because of asynchronous NOTIFYs that can be sent by the backend
|
|
||||||
even if the user didn't asked for them */
|
|
||||||
|
|
||||||
if (self->async_status == ASYNC_READ && self->async_cursor) {
|
|
||||||
cursorObject *curs = (cursorObject *)self->async_cursor;
|
|
||||||
IFCLEARPGRES(curs->pgres);
|
|
||||||
curs->pgres = pq_get_last_result(self);
|
|
||||||
|
|
||||||
/* fetch the tuples (if there are any) and build the result. We don't
|
|
||||||
* care if pq_fetch return 0 or 1, but if there was an error, we want to
|
|
||||||
* signal it to the caller. */
|
|
||||||
last_result = pq_fetch(curs) == -1 ? -1 : 0;
|
|
||||||
|
|
||||||
/* We have finished with our async_cursor */
|
|
||||||
Py_XDECREF(self->async_cursor);
|
|
||||||
self->async_cursor = NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
last_result = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_result == 0) {
|
|
||||||
Dprintf("conn_poll_fetch: returning %d", PSYCO_POLL_OK);
|
|
||||||
/* self->async_status cannot be ASYNC_WRITE here, because we
|
|
||||||
never execute curs_poll_fetch in ASYNC_WRITE state, so we can
|
|
||||||
safely set it to ASYNC_DONE because we either fetched the result or
|
|
||||||
there is no result to fetch */
|
|
||||||
self->async_status = ASYNC_DONE;
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_OK);
|
|
||||||
}
|
|
||||||
else if (last_result == 1) {
|
|
||||||
Dprintf("conn_poll_fetch: got result, but data remaining, "
|
|
||||||
"returning %d", PSYCO_POLL_READ);
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_READ);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* poll during a connection attempt until the connection has established. */
|
/* poll during a connection attempt until the connection has established. */
|
||||||
|
|
||||||
int
|
static int
|
||||||
_conn_poll_connecting(connectionObject *self)
|
_conn_poll_connecting(connectionObject *self)
|
||||||
{
|
{
|
||||||
int res = PSYCO_POLL_ERROR;
|
int res = PSYCO_POLL_ERROR;
|
||||||
|
|
||||||
|
Dprintf("conn_poll: poll connecting");
|
||||||
switch (PQconnectPoll(self->pgconn)) {
|
switch (PQconnectPoll(self->pgconn)) {
|
||||||
case PGRES_POLLING_OK:
|
case PGRES_POLLING_OK:
|
||||||
res = PSYCO_POLL_OK;
|
res = PSYCO_POLL_OK;
|
||||||
|
@ -792,11 +478,13 @@ _conn_poll_connecting(connectionObject *self)
|
||||||
|
|
||||||
|
|
||||||
/* Advance to the next state after an attempt of flushing output */
|
/* Advance to the next state after an attempt of flushing output */
|
||||||
int
|
|
||||||
|
static int
|
||||||
_conn_poll_advance_write(connectionObject *self, int flush)
|
_conn_poll_advance_write(connectionObject *self, int flush)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
Dprintf("conn_poll: poll writing");
|
||||||
switch (flush) {
|
switch (flush) {
|
||||||
case 0: /* success */
|
case 0: /* success */
|
||||||
/* we've finished pushing the query to the server. Let's start
|
/* we've finished pushing the query to the server. Let's start
|
||||||
|
@ -821,11 +509,12 @@ _conn_poll_advance_write(connectionObject *self, int flush)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Advance to the next state after a call to a pq_is_busy* function */
|
/* Advance to the next state after a call to a pq_is_busy* function */
|
||||||
int
|
static int
|
||||||
_conn_poll_advance_read(connectionObject *self, int busy)
|
_conn_poll_advance_read(connectionObject *self, int busy)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
Dprintf("conn_poll: poll reading");
|
||||||
switch (busy) {
|
switch (busy) {
|
||||||
case 0: /* result is ready */
|
case 0: /* result is ready */
|
||||||
res = PSYCO_POLL_OK;
|
res = PSYCO_POLL_OK;
|
||||||
|
@ -850,7 +539,8 @@ _conn_poll_advance_read(connectionObject *self, int busy)
|
||||||
|
|
||||||
Advance the async_status (usually going WRITE -> READ -> DONE) but don't
|
Advance the async_status (usually going WRITE -> READ -> DONE) but don't
|
||||||
mess with the connection status. */
|
mess with the connection status. */
|
||||||
int
|
|
||||||
|
static int
|
||||||
_conn_poll_query(connectionObject *self)
|
_conn_poll_query(connectionObject *self)
|
||||||
{
|
{
|
||||||
int res = PSYCO_POLL_ERROR;
|
int res = PSYCO_POLL_ERROR;
|
||||||
|
@ -891,30 +581,148 @@ _conn_poll_query(connectionObject *self)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Advance to the next state during an async connection setup
|
||||||
/* conn_poll_green - poll a *sync* connection with external wait */
|
*
|
||||||
|
* If the connection is green, this is performed by the regular
|
||||||
PyObject *
|
* sync code so the queries are sent by conn_setup() while in
|
||||||
conn_poll_green(connectionObject *self)
|
* CONN_STATUS_READY state.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
_conn_poll_setup_async(connectionObject *self)
|
||||||
{
|
{
|
||||||
int res = PSYCO_POLL_ERROR;
|
int res = PSYCO_POLL_ERROR;
|
||||||
|
PGresult *pgres;
|
||||||
|
|
||||||
|
switch (self->status) {
|
||||||
|
case CONN_STATUS_CONNECTING:
|
||||||
|
/* Set the connection to nonblocking now. */
|
||||||
|
if (pq_set_non_blocking(self, 1, 1) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->equote = conn_get_standard_conforming_strings(self->pgconn);
|
||||||
|
self->protocol = conn_get_protocol_version(self->pgconn);
|
||||||
|
self->server_version = conn_get_server_version(self->pgconn);
|
||||||
|
|
||||||
|
/* asynchronous connections always use isolation level 0, the user is
|
||||||
|
* expected to manage the transactions himself, by sending
|
||||||
|
* (asynchronously) BEGIN and COMMIT statements.
|
||||||
|
*/
|
||||||
|
self->isolation_level = 0;
|
||||||
|
|
||||||
|
Dprintf("conn_poll: status -> CONN_STATUS_DATESTYLE");
|
||||||
|
self->status = CONN_STATUS_DATESTYLE;
|
||||||
|
if (0 == pq_send_query(self, psyco_datestyle)) {
|
||||||
|
PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Dprintf("conn_poll: async_status -> ASYNC_WRITE");
|
||||||
|
self->async_status = ASYNC_WRITE;
|
||||||
|
res = PSYCO_POLL_WRITE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONN_STATUS_DATESTYLE:
|
||||||
|
res = _conn_poll_query(self);
|
||||||
|
if (res == PSYCO_POLL_OK) {
|
||||||
|
res = PSYCO_POLL_ERROR;
|
||||||
|
pgres = pq_get_last_result(self);
|
||||||
|
if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) {
|
||||||
|
PyErr_SetString(OperationalError, "can't set datestyle to ISO");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CLEARPGRES(pgres);
|
||||||
|
|
||||||
|
Dprintf("conn_poll: status -> CONN_STATUS_CLIENT_ENCODING");
|
||||||
|
self->status = CONN_STATUS_CLIENT_ENCODING;
|
||||||
|
if (0 == pq_send_query(self, psyco_client_encoding)) {
|
||||||
|
PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Dprintf("conn_poll: async_status -> ASYNC_WRITE");
|
||||||
|
self->async_status = ASYNC_WRITE;
|
||||||
|
res = PSYCO_POLL_WRITE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONN_STATUS_CLIENT_ENCODING:
|
||||||
|
res = _conn_poll_query(self);
|
||||||
|
if (res == PSYCO_POLL_OK) {
|
||||||
|
res = PSYCO_POLL_ERROR;
|
||||||
|
pgres = pq_get_last_result(self);
|
||||||
|
if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) {
|
||||||
|
PyErr_SetString(OperationalError, "can't fetch client_encoding");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* conn_get_encoding returns a malloc'd string */
|
||||||
|
self->encoding = conn_get_encoding(pgres);
|
||||||
|
CLEARPGRES(pgres);
|
||||||
|
if (self->encoding == NULL) { break; }
|
||||||
|
|
||||||
|
Dprintf("conn_poll: status -> CONN_STATUS_READY");
|
||||||
|
self->status = CONN_STATUS_READY;
|
||||||
|
res = PSYCO_POLL_OK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* conn_poll - Main polling switch
|
||||||
|
*
|
||||||
|
* The function is called in all the states and connection types and invokes
|
||||||
|
* the right "next step".
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
conn_poll(connectionObject *self)
|
||||||
|
{
|
||||||
|
int res = PSYCO_POLL_ERROR;
|
||||||
|
Dprintf("conn_poll: status = %d", self->status);
|
||||||
|
|
||||||
switch (self->status) {
|
switch (self->status) {
|
||||||
case CONN_STATUS_SETUP:
|
case CONN_STATUS_SETUP:
|
||||||
Dprintf("conn_poll: status = CONN_STATUS_SETUP");
|
Dprintf("conn_poll: status -> CONN_STATUS_CONNECTING");
|
||||||
self->status = CONN_STATUS_CONNECTING;
|
self->status = CONN_STATUS_CONNECTING;
|
||||||
res = PSYCO_POLL_WRITE;
|
res = PSYCO_POLL_WRITE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONN_STATUS_CONNECTING:
|
case CONN_STATUS_CONNECTING:
|
||||||
Dprintf("conn_poll: status = CONN_STATUS_CONNECTING");
|
|
||||||
res = _conn_poll_connecting(self);
|
res = _conn_poll_connecting(self);
|
||||||
|
if (res == PSYCO_POLL_OK && self->async) {
|
||||||
|
res = _conn_poll_setup_async(self);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONN_STATUS_DATESTYLE:
|
||||||
|
case CONN_STATUS_CLIENT_ENCODING:
|
||||||
|
res = _conn_poll_setup_async(self);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONN_STATUS_READY:
|
case CONN_STATUS_READY:
|
||||||
case CONN_STATUS_BEGIN:
|
case CONN_STATUS_BEGIN:
|
||||||
Dprintf("conn_poll: status = CONN_STATUS_READY/BEGIN");
|
|
||||||
res = _conn_poll_query(self);
|
res = _conn_poll_query(self);
|
||||||
|
|
||||||
|
if (res == PSYCO_POLL_OK && self->async_cursor) {
|
||||||
|
/* An async query has just finished: parse the tuple in the
|
||||||
|
* target cursor. */
|
||||||
|
cursorObject *curs = (cursorObject *)self->async_cursor;
|
||||||
|
IFCLEARPGRES(curs->pgres);
|
||||||
|
curs->pgres = pq_get_last_result(self);
|
||||||
|
|
||||||
|
/* fetch the tuples (if there are any) and build the result. We
|
||||||
|
* don't care if pq_fetch return 0 or 1, but if there was an error,
|
||||||
|
* we want to signal it to the caller. */
|
||||||
|
if (pq_fetch(curs) == -1) {
|
||||||
|
res = PSYCO_POLL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have finished with our async_cursor */
|
||||||
|
Py_XDECREF(self->async_cursor);
|
||||||
|
self->async_cursor = NULL;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -922,12 +730,7 @@ conn_poll_green(connectionObject *self)
|
||||||
res = PSYCO_POLL_ERROR;
|
res = PSYCO_POLL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(res == PSYCO_POLL_ERROR && PyErr_Occurred())) {
|
return res;
|
||||||
return PyInt_FromLong(res);
|
|
||||||
} else {
|
|
||||||
/* There is an error and an exception is already in place */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* conn_close - do anything needed to shut down the connection */
|
/* conn_close - do anything needed to shut down the connection */
|
||||||
|
|
|
@ -408,129 +408,19 @@ psyco_conn_get_exception(PyObject *self, void *closure)
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define psyco_conn_poll_doc \
|
|
||||||
"poll() -- return POLL_OK if the operation has finished, " \
|
|
||||||
"POLL_READ if the application should be waiting " \
|
|
||||||
"for the socket to be readable or POLL_WRITE " \
|
|
||||||
"if the socket should be writable."
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
psyco_conn_poll_async(connectionObject *self)
|
|
||||||
{
|
|
||||||
PostgresPollingStatusType poll_status;
|
|
||||||
|
|
||||||
Dprintf("conn_poll: polling with status %d", self->status);
|
|
||||||
|
|
||||||
switch (self->status) {
|
|
||||||
|
|
||||||
case CONN_STATUS_SETUP:
|
|
||||||
/* according to libpq documentation the user should start by waiting
|
|
||||||
for the socket to become writable */
|
|
||||||
self->status = CONN_STATUS_CONNECTING;
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_WRITE);
|
|
||||||
|
|
||||||
case CONN_STATUS_CONNECTING:
|
|
||||||
/* this means we are in the middle of a PQconnectPoll loop */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONN_STATUS_SEND_DATESTYLE:
|
|
||||||
case CONN_STATUS_SENT_DATESTYLE:
|
|
||||||
case CONN_STATUS_SEND_CLIENT_ENCODING:
|
|
||||||
case CONN_STATUS_SENT_CLIENT_ENCODING:
|
|
||||||
/* these mean that we need to wait for the socket to become writable
|
|
||||||
to send the rest of our query */
|
|
||||||
return conn_poll_connect_send(self);
|
|
||||||
|
|
||||||
case CONN_STATUS_GET_DATESTYLE:
|
|
||||||
case CONN_STATUS_GET_CLIENT_ENCODING:
|
|
||||||
/* these mean that we are waiting for the results of the queries */
|
|
||||||
return conn_poll_connect_fetch(self);
|
|
||||||
|
|
||||||
case CONN_STATUS_READY:
|
|
||||||
case CONN_STATUS_BEGIN:
|
|
||||||
/* The connection is ready, but we might be in an asynchronous query,
|
|
||||||
or we just might want to check for NOTIFYs. For synchronous
|
|
||||||
connections the status might be BEGIN, not READY. */
|
|
||||||
return conn_poll_ready(self);
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* everything else is an error */
|
|
||||||
PyErr_SetString(OperationalError,
|
|
||||||
"not in asynchronous connection attempt");
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
pthread_mutex_lock(&self->lock);
|
|
||||||
|
|
||||||
poll_status = PQconnectPoll(self->pgconn);
|
|
||||||
|
|
||||||
if (poll_status == PGRES_POLLING_READING) {
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_BLOCK_THREADS;
|
|
||||||
Dprintf("conn_poll: returing POLL_READ");
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (poll_status == PGRES_POLLING_WRITING) {
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_BLOCK_THREADS;
|
|
||||||
Dprintf("conn_poll: returing POLL_WRITE");
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_WRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (poll_status == PGRES_POLLING_FAILED) {
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_BLOCK_THREADS;
|
|
||||||
PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the only other thing that PQconnectPoll can return is PGRES_POLLING_OK,
|
|
||||||
but make sure */
|
|
||||||
if (poll_status != PGRES_POLLING_OK) {
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_BLOCK_THREADS;
|
|
||||||
PyErr_Format(OperationalError,
|
|
||||||
"unexpected result from PQconnectPoll: %d", poll_status);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dprintf("conn_poll: got POLL_OK");
|
|
||||||
|
|
||||||
/* the connection is built, but we want to do a few other things before we
|
|
||||||
let the user use it */
|
|
||||||
|
|
||||||
self->equote = conn_get_standard_conforming_strings(self->pgconn);
|
|
||||||
|
|
||||||
Dprintf("conn_poll: got standard_conforming_strings");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here is the tricky part, we need to figure the datestyle,
|
|
||||||
* client_encoding and isolevel, all using nonblocking calls. To do that
|
|
||||||
* we will keep telling the user to poll, while we are waiting for our
|
|
||||||
* asynchronous queries to complete.
|
|
||||||
*/
|
|
||||||
pthread_mutex_unlock(&(self->lock));
|
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
/* the next operation the client will do is send a query, so ask him to
|
|
||||||
wait for a writable condition */
|
|
||||||
self->status = CONN_STATUS_SEND_DATESTYLE;
|
|
||||||
Dprintf("conn_poll: connection is built, retrning %d",
|
|
||||||
PSYCO_POLL_WRITE);
|
|
||||||
return PyInt_FromLong(PSYCO_POLL_WRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
psyco_conn_poll(connectionObject *self)
|
psyco_conn_poll(connectionObject *self)
|
||||||
{
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
EXC_IF_CONN_CLOSED(self);
|
EXC_IF_CONN_CLOSED(self);
|
||||||
|
|
||||||
if (self->async) {
|
res = conn_poll(self);
|
||||||
return psyco_conn_poll_async(self);
|
if (res != PSYCO_POLL_ERROR || !PyErr_Occurred()) {
|
||||||
|
return PyInt_FromLong(res);
|
||||||
} else {
|
} else {
|
||||||
return conn_poll_green(self);
|
/* There is an error and an exception is already in place */
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,14 +147,13 @@ psyco_exec_green(connectionObject *conn, const char *command)
|
||||||
PGresult *result = NULL;
|
PGresult *result = NULL;
|
||||||
PyObject *cb, *pyrv;
|
PyObject *cb, *pyrv;
|
||||||
|
|
||||||
|
Dprintf("psyco_exec_green: executing query async");
|
||||||
if (!(cb = have_wait_callback())) {
|
if (!(cb = have_wait_callback())) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send the query asynchronously */
|
/* Send the query asynchronously */
|
||||||
Dprintf("psyco_exec_green: sending query async");
|
if (0 == pq_send_query(conn, command)) {
|
||||||
if (0 == PQsendQuery(conn->pgconn, command)) {
|
|
||||||
Dprintf("psyco_exec_green: PQsendQuery returned 0");
|
|
||||||
goto clear;
|
goto clear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -803,6 +803,27 @@ pq_execute(cursorObject *curs, const char *query, int async)
|
||||||
return 1-async;
|
return 1-async;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* send an async query to the backend.
|
||||||
|
*
|
||||||
|
* Return 1 if command succeeded, else 0.
|
||||||
|
*
|
||||||
|
* The function should be called helding the connection lock and the GIL.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
pq_send_query(connectionObject *conn, const char *query)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
Dprintf("pq_send_query: sending ASYNC query:");
|
||||||
|
Dprintf(" %-.200s", query);
|
||||||
|
|
||||||
|
if (0 == (rv = PQsendQuery(conn->pgconn, query))) {
|
||||||
|
Dprintf("pq_send_query: error: %s", PQerrorMessage(conn->pgconn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return the last result available on the connection.
|
/* Return the last result available on the connection.
|
||||||
*
|
*
|
||||||
* The function will block will block only if a command is active and the
|
* The function will block will block only if a command is active and the
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
HIDDEN PGresult *pq_get_last_result(connectionObject *conn);
|
HIDDEN PGresult *pq_get_last_result(connectionObject *conn);
|
||||||
HIDDEN int pq_fetch(cursorObject *curs);
|
HIDDEN int pq_fetch(cursorObject *curs);
|
||||||
HIDDEN int pq_execute(cursorObject *curs, const char *query, int async);
|
HIDDEN int pq_execute(cursorObject *curs, const char *query, int async);
|
||||||
|
HIDDEN int pq_send_query(connectionObject *conn, const char *query);
|
||||||
HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres,
|
HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres,
|
||||||
char **error, PyThreadState **tstate);
|
char **error, PyThreadState **tstate);
|
||||||
HIDDEN int pq_commit(connectionObject *conn);
|
HIDDEN int pq_commit(connectionObject *conn);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user