diff --git a/NEWS b/NEWS index ff9d0eb1..1c439323 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ What's new in psycopg 2.4.2 --------------------------- + - Allow using the isolation level "repeatable read" which is distinct + from "serializable" in PostgreSQL 9.1. - Don't build mx.DateTime support if the module can't be imported (ticket #53). diff --git a/lib/extensions.py b/lib/extensions.py index 82e17fa4..cb48b793 100644 --- a/lib/extensions.py +++ b/lib/extensions.py @@ -68,13 +68,11 @@ except ImportError: pass """Isolation level values.""" -ISOLATION_LEVEL_AUTOCOMMIT = 0 -ISOLATION_LEVEL_READ_COMMITTED = 1 -ISOLATION_LEVEL_SERIALIZABLE = 2 - -# PostgreSQL maps the the other standard values to already defined levels -ISOLATION_LEVEL_REPEATABLE_READ = ISOLATION_LEVEL_SERIALIZABLE -ISOLATION_LEVEL_READ_UNCOMMITTED = ISOLATION_LEVEL_READ_COMMITTED +ISOLATION_LEVEL_AUTOCOMMIT = 0 +ISOLATION_LEVEL_READ_UNCOMMITTED = 1 +ISOLATION_LEVEL_READ_COMMITTED = 2 +ISOLATION_LEVEL_REPEATABLE_READ = 3 +ISOLATION_LEVEL_SERIALIZABLE = 4 """psycopg connection status values.""" STATUS_SETUP = 0 diff --git a/psycopg/connection.h b/psycopg/connection.h index 552b93d7..262e6ac2 100644 --- a/psycopg/connection.h +++ b/psycopg/connection.h @@ -63,9 +63,11 @@ extern "C" { /* possible values for isolation_level */ typedef enum { - ISOLATION_LEVEL_AUTOCOMMIT = 0, - ISOLATION_LEVEL_READ_COMMITTED = 1, - ISOLATION_LEVEL_SERIALIZABLE = 2, + ISOLATION_LEVEL_AUTOCOMMIT = 0, + ISOLATION_LEVEL_READ_UNCOMMITTED = 1, + ISOLATION_LEVEL_READ_COMMITTED = 2, + ISOLATION_LEVEL_REPEATABLE_READ = 3, + ISOLATION_LEVEL_SERIALIZABLE = 4, } conn_isolation_level_t; extern HIDDEN PyTypeObject connectionType; diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 7ca395dc..2d02b86b 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -400,9 +400,9 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "i", &level)) return NULL; - if (level < 0 || level > 2) { + if (level < 0 || level > 4) { PyErr_SetString(PyExc_ValueError, - "isolation level must be between 0 and 2"); + "isolation level must be between 0 and 4"); return NULL; } diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c index 6a6d05a3..a6144229 100644 --- a/psycopg/pqpath.c +++ b/psycopg/pqpath.c @@ -408,7 +408,9 @@ pq_begin_locked(connectionObject *conn, PGresult **pgres, char **error, { const char *query[] = { NULL, + "BEGIN; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED", "BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED", + "BEGIN; SET TRANSACTION ISOLATION LEVEL REPEATABLE READ", "BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"}; int result; diff --git a/tests/test_connection.py b/tests/test_connection.py index d9da471f..89b104f4 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -201,24 +201,30 @@ class IsolationLevelsTestCase(unittest.TestCase): def test_set_isolation_level(self): conn = self.connect() + curs = conn.cursor() - conn.set_isolation_level( - psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) - self.assertEqual(conn.isolation_level, - psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + for name, level in ( + (None, psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT), + ('read uncommitted', psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED), + ('read committed', psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED), + ('repeatable read', psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ), + ('serializable', psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE), + ): + conn.set_isolation_level(level) + self.assertEqual(conn.isolation_level, level) - conn.set_isolation_level( - psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) - self.assertEqual(conn.isolation_level, - psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) + curs.execute('show transaction_isolation;') + got_name = curs.fetchone()[0] - conn.set_isolation_level( - psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) - self.assertEqual(conn.isolation_level, - psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + if name is None: + curs.execute('show default_transaction_isolation;') + name = curs.fetchone()[0] + + self.assertEqual(name, got_name) + conn.commit() self.assertRaises(ValueError, conn.set_isolation_level, -1) - self.assertRaises(ValueError, conn.set_isolation_level, 3) + self.assertRaises(ValueError, conn.set_isolation_level, 5) def test_set_isolation_level_abort(self): conn = self.connect()