X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=psycopg2%2Fpsycopg%2Fconnection_int.c;fp=psycopg2%2Fpsycopg%2Fconnection_int.c;h=3e61ddf1798921dec9be078deb35aa572876d299;hb=e5bdc26e1423689c0ab3204931335787737946ea;hp=0000000000000000000000000000000000000000;hpb=f8dd312990da7cc744e1c148bfd395c18492f3f1;p=plcapi.git diff --git a/psycopg2/psycopg/connection_int.c b/psycopg2/psycopg/connection_int.c new file mode 100644 index 0000000..3e61ddf --- /dev/null +++ b/psycopg2/psycopg/connection_int.c @@ -0,0 +1,303 @@ +/* connection_int.c - code used by the connection object + * + * Copyright (C) 2003 Federico Di Gregorio + * + * This file is part of psycopg. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#define PSYCOPG_MODULE +#include "psycopg/config.h" +#include "psycopg/psycopg.h" +#include "psycopg/connection.h" +#include "psycopg/cursor.h" +#include "psycopg/pqpath.h" + +/* conn_notice_callback - process notices */ + +void +conn_notice_callback(void *args, const char *message) +{ + connectionObject *self = (connectionObject *)args; + + Dprintf("conn_notice_callback: %s", message); + + /* unfortunately the old protocl return COPY FROM errors only as notices, + so we need to filter them looking for such errors */ + if (strncmp(message, "ERROR", 5) == 0) + pq_set_critical(self, message); + else + PyList_Append(self->notice_list, PyString_FromString(message)); +} + +/* conn_connect - execute a connection to the dataabase */ + +int +conn_connect(connectionObject *self) +{ + PGconn *pgconn; + PGresult *pgres; + char *data, *tmp; + int i; + + /* we need the initial date style to be ISO, for typecasters; if the user + later change it, she must know what she's doing... */ + const char *datestyle = "SET DATESTYLE TO 'ISO'"; + const char *encoding = "SHOW client_encoding"; + const char *isolevel = "SHOW default_transaction_isolation"; + + const char *lvl1a = "read uncommitted"; + const char *lvl1b = "read committed"; + const char *lvl2a = "repeatable read"; + const char *lvl2b = "serializable"; + + Py_BEGIN_ALLOW_THREADS; + pgconn = PQconnectdb(self->dsn); + Py_END_ALLOW_THREADS; + + Dprintf("conn_connect: new postgresql connection at %p", pgconn); + + if (pgconn == NULL) + { + Dprintf("conn_connect: PQconnectdb(%s) FAILED", self->dsn); + PyErr_SetString(OperationalError, "PQconnectdb() failed"); + return -1; + } + else if (PQstatus(pgconn) == CONNECTION_BAD) + { + Dprintf("conn_connect: PQconnectdb(%s) returned BAD", self->dsn); + PyErr_SetString(OperationalError, PQerrorMessage(pgconn)); + PQfinish(pgconn); + return -1; + } + + PQsetNoticeProcessor(pgconn, conn_notice_callback, (void*)self); + + Py_BEGIN_ALLOW_THREADS; + pgres = PQexec(pgconn, datestyle); + Py_END_ALLOW_THREADS; + + if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) { + PyErr_SetString(OperationalError, "can't set datestyle to ISO"); + PQfinish(pgconn); + IFCLEARPGRES(pgres); + return -1; + } + CLEARPGRES(pgres); + + Py_BEGIN_ALLOW_THREADS; + pgres = PQexec(pgconn, encoding); + Py_END_ALLOW_THREADS; + + if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) { + PyErr_SetString(OperationalError, "can't fetch client_encoding"); + PQfinish(pgconn); + IFCLEARPGRES(pgres); + return -1; + } + tmp = PQgetvalue(pgres, 0, 0); + self->encoding = PyMem_Malloc(strlen(tmp)+1); + if (self->encoding == NULL) { + /* exception already set by PyMem_Malloc() */ + PQfinish(pgconn); + IFCLEARPGRES(pgres); + return -1; + } + for (i=0 ; i < strlen(tmp) ; i++) + self->encoding[i] = toupper(tmp[i]); + self->encoding[i] = '\0'; + CLEARPGRES(pgres); + + Py_BEGIN_ALLOW_THREADS; + pgres = PQexec(pgconn, isolevel); + Py_END_ALLOW_THREADS; + + if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) { + PyErr_SetString(OperationalError, + "can't fetch default_isolation_level"); + PQfinish(pgconn); + IFCLEARPGRES(pgres); + return -1; + } + data = PQgetvalue(pgres, 0, 0); + if ((strncmp(lvl1a, data, strlen(lvl1a)) == 0) + || (strncmp(lvl1b, data, strlen(lvl1b)) == 0)) + self->isolation_level = 1; + else if ((strncmp(lvl2a, data, strlen(lvl2a)) == 0) + || (strncmp(lvl2b, data, strlen(lvl2b)) == 0)) + self->isolation_level = 2; + else + self->isolation_level = 2; + CLEARPGRES(pgres); + + if (PQsetnonblocking(pgconn, 1) != 0) { + Dprintf("conn_connect: PQsetnonblocking() FAILED"); + PyErr_SetString(OperationalError, "PQsetnonblocking() failed"); + PQfinish(pgconn); + return -1; + } + +#ifdef HAVE_PQPROTOCOL3 + self->protocol = PQprotocolVersion(pgconn); +#else + self->protocol = 2; +#endif + Dprintf("conn_connect: using protocol %d", self->protocol); + + self->pgconn = pgconn; + return 0; +} + +/* conn_close - do anything needed to shut down the connection */ + +void +conn_close(connectionObject *self) +{ + /* 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 + the connection fails! */ + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + self->closed = 1; + + /* execute a forced rollback on the connection (but don't check the + result, we're going to close the pq connection anyway */ + if (self->pgconn) { + pq_abort(self); + PQfinish(self->pgconn); + Dprintf("conn_close: PQfinish called"); + self->pgconn = NULL; + } + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + +} + +/* conn_commit - commit on a connection */ + +int +conn_commit(connectionObject *self) +{ + int res; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + res = pq_commit(self); + self->mark++; + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + return res; +} + +/* conn_rollback - rollback a connection */ + +int +conn_rollback(connectionObject *self) +{ + int res; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + res = pq_abort(self); + self->mark++; + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + return res; +} + +/* conn_switch_isolation_level - switch isolation level on the connection */ + +int +conn_switch_isolation_level(connectionObject *self, int level) +{ + int res = 0; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + /* if the current isolation level is > 0 we need to abort the current + transaction before changing; that all folks! */ + if (self->isolation_level != level && self->isolation_level > 0) { + res = pq_abort(self); + } + self->isolation_level = level; + self->mark++; + + Dprintf("conn_switch_isolation_level: switched to level %d", level); + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + return res; +} + +/* conn_set_client_encoding - switch client encoding on connection */ + +int +conn_set_client_encoding(connectionObject *self, char *enc) +{ + PGresult *pgres; + char query[48]; + int res = 0; + + /* TODO: check for async query here and raise error if necessary */ + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + /* set encoding, no encoding string is longer than 24 bytes */ + PyOS_snprintf(query, 47, "SET client_encoding = '%s'", enc); + + /* abort the current transaction, to set the encoding ouside of + transactions */ + res = pq_abort(self); + + if (res == 0) { + pgres = PQexec(self->pgconn, query); + + if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) { + res = -1; + } + else { + /* no error, we can proceeed and store the new encoding */ + if (self->encoding) free(self->encoding); + self->encoding = strdup(enc); + } + + IFCLEARPGRES(pgres); + } + + Dprintf("conn_set_client_encoding: set encoding to %s", self->encoding); + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + if (res == -1) + PyErr_Format(OperationalError, "can't set encoding to %s", enc); + + return res; +}