mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 18:33:44 +03:00
Added enum with possilbe isolation level states.
Also, general isolation levels cleanup and tests added.
This commit is contained in:
parent
4074635629
commit
bcacdc8461
|
@ -1,3 +1,8 @@
|
|||
2010-11-18 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||
|
||||
* psycopg/connection.h: Added enum with possilbe isolation level states.
|
||||
Also, general isolation levels cleanup and tests added.
|
||||
|
||||
2010-11-17 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||
|
||||
* psycopg/connection_type.c: don't clobber exception if
|
||||
|
|
|
@ -66,6 +66,13 @@ extern "C" {
|
|||
#define psyco_datestyle "SET DATESTYLE TO 'ISO'"
|
||||
#define psyco_transaction_isolation "SHOW default_transaction_isolation"
|
||||
|
||||
/* possible values for isolation_level */
|
||||
typedef enum {
|
||||
ISOLATION_LEVEL_AUTOCOMMIT = 0,
|
||||
ISOLATION_LEVEL_READ_COMMITTED = 1,
|
||||
ISOLATION_LEVEL_SERIALIZABLE = 2,
|
||||
} conn_isolation_level_t;
|
||||
|
||||
extern HIDDEN PyTypeObject connectionType;
|
||||
|
||||
struct connectionObject_notice {
|
||||
|
|
|
@ -265,11 +265,11 @@ conn_get_isolation_level(PGresult *pgres)
|
|||
|
||||
char *isolation_level = PQgetvalue(pgres, 0, 0);
|
||||
|
||||
if ((strncmp(lvl1a, isolation_level, strlen(isolation_level)) == 0)
|
||||
|| (strncmp(lvl1b, isolation_level, strlen(isolation_level)) == 0))
|
||||
rv = 1;
|
||||
if ((strcmp(lvl1b, isolation_level) == 0) /* most likely */
|
||||
|| (strcmp(lvl1a, isolation_level) == 0))
|
||||
rv = ISOLATION_LEVEL_READ_COMMITTED;
|
||||
else /* if it's not one of the lower ones, it's SERIALIZABLE */
|
||||
rv = 2;
|
||||
rv = ISOLATION_LEVEL_SERIALIZABLE;
|
||||
|
||||
CLEARPGRES(pgres);
|
||||
|
||||
|
@ -659,7 +659,7 @@ _conn_poll_setup_async(connectionObject *self)
|
|||
* expected to manage the transactions himself, by sending
|
||||
* (asynchronously) BEGIN and COMMIT statements.
|
||||
*/
|
||||
self->isolation_level = 0;
|
||||
self->isolation_level = ISOLATION_LEVEL_AUTOCOMMIT;
|
||||
|
||||
/* If the datestyle is ISO or anything else good,
|
||||
* we can skip the CONN_STATUS_DATESTYLE step. */
|
||||
|
@ -830,20 +830,21 @@ conn_switch_isolation_level(connectionObject *self, int level)
|
|||
char *error = NULL;
|
||||
int res = 0;
|
||||
|
||||
/* if the current isolation level is equal to the requested one don't switch */
|
||||
if (self->isolation_level == level) return 0;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
pthread_mutex_lock(&self->lock);
|
||||
|
||||
/* if the current isolation level is equal to the requested one don't switch */
|
||||
if (self->isolation_level != level) {
|
||||
|
||||
/* 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) {
|
||||
if (self->isolation_level != ISOLATION_LEVEL_AUTOCOMMIT) {
|
||||
res = pq_abort_locked(self, &pgres, &error, &_save);
|
||||
}
|
||||
self->isolation_level = level;
|
||||
|
||||
Dprintf("conn_switch_isolation_level: switched to level %d", level);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&self->lock);
|
||||
Py_END_ALLOW_THREADS;
|
||||
|
|
|
@ -209,7 +209,7 @@ psyco_conn_tpc_begin(connectionObject *self, PyObject *args)
|
|||
}
|
||||
|
||||
/* two phase commit and autocommit make no point */
|
||||
if (self->isolation_level == 0) {
|
||||
if (self->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT) {
|
||||
PyErr_SetString(ProgrammingError,
|
||||
"tpc_begin can't be called in autocommit mode");
|
||||
goto exit;
|
||||
|
@ -410,7 +410,7 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
|
|||
|
||||
if (level < 0 || level > 2) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"isolation level out of bounds (0,3)");
|
||||
"isolation level must be between 0 and 2");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -476,7 +476,7 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
|
|||
NULL, NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (self->conn->isolation_level == 0) {
|
||||
if (self->conn->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT) {
|
||||
psyco_set_error(ProgrammingError, (PyObject*)self,
|
||||
"can't use a named cursor outside of transactions", NULL, NULL);
|
||||
return NULL;
|
||||
|
|
|
@ -129,7 +129,7 @@ lobject_close_locked(lobjectObject *self, char **error)
|
|||
{
|
||||
int retvalue;
|
||||
|
||||
if (self->conn->isolation_level == 0 ||
|
||||
if (self->conn->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT ||
|
||||
self->conn->mark != self->mark ||
|
||||
self->fd == -1)
|
||||
return 0;
|
||||
|
|
|
@ -55,7 +55,7 @@ psyco_lobj_close(lobjectObject *self, PyObject *args)
|
|||
closing the current transaction is equivalent to close all the
|
||||
opened large objects */
|
||||
if (!lobject_is_closed(self)
|
||||
&& self->conn->isolation_level > 0
|
||||
&& self->conn->isolation_level != ISOLATION_LEVEL_AUTOCOMMIT
|
||||
&& self->conn->mark == self->mark)
|
||||
{
|
||||
Dprintf("psyco_lobj_close: closing lobject at %p", self);
|
||||
|
@ -300,7 +300,7 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
|
|||
{
|
||||
Dprintf("lobject_setup: init lobject object at %p", self);
|
||||
|
||||
if (conn->isolation_level == 0) {
|
||||
if (conn->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT) {
|
||||
psyco_set_error(ProgrammingError, (PyObject*)self,
|
||||
"can't use a lobject outside of transactions", NULL, NULL);
|
||||
return -1;
|
||||
|
|
|
@ -409,7 +409,8 @@ pq_begin_locked(connectionObject *conn, PGresult **pgres, char **error,
|
|||
Dprintf("pq_begin_locked: pgconn = %p, isolevel = %ld, status = %d",
|
||||
conn->pgconn, conn->isolation_level, conn->status);
|
||||
|
||||
if (conn->isolation_level == 0 || conn->status != CONN_STATUS_READY) {
|
||||
if (conn->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT
|
||||
|| conn->status != CONN_STATUS_READY) {
|
||||
Dprintf("pq_begin_locked: transaction in progress");
|
||||
return 0;
|
||||
}
|
||||
|
@ -438,7 +439,8 @@ pq_commit(connectionObject *conn)
|
|||
Dprintf("pq_commit: pgconn = %p, isolevel = %ld, status = %d",
|
||||
conn->pgconn, conn->isolation_level, conn->status);
|
||||
|
||||
if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) {
|
||||
if (conn->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT
|
||||
|| conn->status != CONN_STATUS_BEGIN) {
|
||||
Dprintf("pq_commit: no transaction to commit");
|
||||
return 0;
|
||||
}
|
||||
|
@ -473,7 +475,8 @@ pq_abort_locked(connectionObject *conn, PGresult **pgres, char **error,
|
|||
Dprintf("pq_abort_locked: pgconn = %p, isolevel = %ld, status = %d",
|
||||
conn->pgconn, conn->isolation_level, conn->status);
|
||||
|
||||
if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) {
|
||||
if (conn->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT
|
||||
|| conn->status != CONN_STATUS_BEGIN) {
|
||||
Dprintf("pq_abort_locked: no transaction to abort");
|
||||
return 0;
|
||||
}
|
||||
|
@ -501,7 +504,8 @@ pq_abort(connectionObject *conn)
|
|||
Dprintf("pq_abort: pgconn = %p, isolevel = %ld, status = %d",
|
||||
conn->pgconn, conn->isolation_level, conn->status);
|
||||
|
||||
if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) {
|
||||
if (conn->isolation_level == ISOLATION_LEVEL_AUTOCOMMIT
|
||||
|| conn->status != CONN_STATUS_BEGIN) {
|
||||
Dprintf("pq_abort: no transaction to abort");
|
||||
return 0;
|
||||
}
|
||||
|
@ -542,7 +546,8 @@ pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error,
|
|||
|
||||
conn->mark += 1;
|
||||
|
||||
if (conn->isolation_level > 0 && conn->status == CONN_STATUS_BEGIN) {
|
||||
if (conn->isolation_level != ISOLATION_LEVEL_AUTOCOMMIT
|
||||
&& conn->status == CONN_STATUS_BEGIN) {
|
||||
retvalue = pq_execute_command_locked(conn, "ABORT", pgres, error, tstate);
|
||||
if (retvalue != 0) return retvalue;
|
||||
}
|
||||
|
|
|
@ -82,6 +82,20 @@ class ConnectionTests(unittest.TestCase):
|
|||
conn = self.connect()
|
||||
self.assert_(conn.protocol_version in (2,3), conn.protocol_version)
|
||||
|
||||
|
||||
class IsolationLevelsTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
conn = self.connect()
|
||||
cur = conn.cursor()
|
||||
cur.execute("drop table if exists isolevel;")
|
||||
cur.execute("create table isolevel (id integer);")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def connect(self):
|
||||
return psycopg2.connect(tests.dsn)
|
||||
|
||||
def test_isolation_level(self):
|
||||
conn = self.connect()
|
||||
self.assertEqual(
|
||||
|
@ -92,22 +106,78 @@ class ConnectionTests(unittest.TestCase):
|
|||
conn = self.connect()
|
||||
self.assert_(conn.encoding in psycopg2.extensions.encodings)
|
||||
|
||||
def test_set_isolation_level(self):
|
||||
conn = self.connect()
|
||||
|
||||
conn.set_isolation_level(
|
||||
psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
self.assertEqual(conn.isolation_level,
|
||||
psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
|
||||
conn.set_isolation_level(
|
||||
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
|
||||
self.assertEqual(conn.isolation_level,
|
||||
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
|
||||
|
||||
conn.set_isolation_level(
|
||||
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
|
||||
self.assertEqual(conn.isolation_level,
|
||||
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
|
||||
|
||||
self.assertRaises(ValueError, conn.set_isolation_level, -1)
|
||||
self.assertRaises(ValueError, conn.set_isolation_level, 3)
|
||||
|
||||
def test_set_isolation_level_abort(self):
|
||||
conn = self.connect()
|
||||
cur = conn.cursor()
|
||||
|
||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||
conn.get_transaction_status())
|
||||
cur.execute("insert into isolevel values (10);")
|
||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS,
|
||||
conn.get_transaction_status())
|
||||
|
||||
conn.set_isolation_level(
|
||||
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
|
||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||
conn.get_transaction_status())
|
||||
cur.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(0, cur.fetchone()[0])
|
||||
|
||||
cur.execute("insert into isolevel values (10);")
|
||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS,
|
||||
conn.get_transaction_status())
|
||||
conn.set_isolation_level(
|
||||
psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||
conn.get_transaction_status())
|
||||
cur.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(0, cur.fetchone()[0])
|
||||
|
||||
cur.execute("insert into isolevel values (10);")
|
||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||
conn.get_transaction_status())
|
||||
conn.set_isolation_level(
|
||||
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
|
||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||
conn.get_transaction_status())
|
||||
cur.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur.fetchone()[0])
|
||||
|
||||
def test_isolation_level_autocommit(self):
|
||||
cnn1 = self.connect()
|
||||
cnn2 = self.connect()
|
||||
cnn2.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
|
||||
cur1 = cnn1.cursor()
|
||||
cur1.execute("drop table if exists isolevel;")
|
||||
cur1.execute("create table isolevel (id integer);")
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(0, cur1.fetchone()[0])
|
||||
cnn1.commit()
|
||||
|
||||
cur2 = cnn2.cursor()
|
||||
cur2.execute("insert into isolevel values (10);")
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur1.fetchone()[0])
|
||||
|
||||
def test_isolation_level_read_committed(self):
|
||||
|
@ -116,47 +186,52 @@ class ConnectionTests(unittest.TestCase):
|
|||
cnn2.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
|
||||
|
||||
cur1 = cnn1.cursor()
|
||||
cur1.execute("drop table if exists isolevel;")
|
||||
cur1.execute("create table isolevel (id integer);")
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(0, cur1.fetchone()[0])
|
||||
cnn1.commit()
|
||||
|
||||
cur2 = cnn2.cursor()
|
||||
cur2.execute("insert into isolevel values (10);")
|
||||
cur1.execute("insert into isolevel values (20);")
|
||||
|
||||
cur2.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur2.fetchone()[0])
|
||||
|
||||
cur1.execute("insert into isolevel values (20);")
|
||||
cnn1.commit()
|
||||
|
||||
cur2.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(2, cur2.fetchone()[0])
|
||||
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur1.fetchone()[0])
|
||||
cnn2.commit()
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(2, cur1.fetchone()[0])
|
||||
|
||||
def test_isolation_level_serializable(self):
|
||||
cnn1 = self.connect()
|
||||
cnn2 = self.connect()
|
||||
cnn2.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
|
||||
|
||||
cur1 = cnn1.cursor()
|
||||
cur1.execute("drop table if exists isolevel;")
|
||||
cur1.execute("create table isolevel (id integer);")
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(0, cur1.fetchone()[0])
|
||||
cnn1.commit()
|
||||
|
||||
cur2 = cnn2.cursor()
|
||||
cur2.execute("insert into isolevel values (10);")
|
||||
cur2.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur2.fetchone()[0])
|
||||
|
||||
cur1.execute("insert into isolevel values (20);")
|
||||
cnn1.commit()
|
||||
|
||||
cur2.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur2.fetchone()[0])
|
||||
cnn1.commit()
|
||||
cur2.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur2.fetchone()[0])
|
||||
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(1, cur1.fetchone()[0])
|
||||
cnn2.commit()
|
||||
cur1.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(2, cur1.fetchone()[0])
|
||||
|
||||
cur2.execute("select count(*) from isolevel;")
|
||||
self.assertEqual(2, cur2.fetchone()[0])
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user