diff --git a/lib/extensions.py b/lib/extensions.py index bedb2463..bb8e3dd6 100644 --- a/lib/extensions.py +++ b/lib/extensions.py @@ -69,13 +69,11 @@ except ImportError: """Isolation level values.""" ISOLATION_LEVEL_AUTOCOMMIT = 0 +ISOLATION_LEVEL_READ_UNCOMMITTED = 4 ISOLATION_LEVEL_READ_COMMITTED = 1 ISOLATION_LEVEL_REPEATABLE_READ = 2 ISOLATION_LEVEL_SERIALIZABLE = 3 -# PostgreSQL internally converts uncommited to commited -ISOLATION_LEVEL_READ_UNCOMMITTED = ISOLATION_LEVEL_READ_COMMITTED - """psycopg connection status values.""" STATUS_SETUP = 0 STATUS_READY = 1 diff --git a/psycopg/connection.h b/psycopg/connection.h index 7f512a18..9a9ea420 100644 --- a/psycopg/connection.h +++ b/psycopg/connection.h @@ -32,6 +32,13 @@ extern "C" { #endif +/* isolation levels */ +#define ISOLATION_LEVEL_AUTOCOMMIT 0 +#define ISOLATION_LEVEL_READ_UNCOMMITTED 4 +#define ISOLATION_LEVEL_READ_COMMITTED 1 +#define ISOLATION_LEVEL_REPEATABLE_READ 2 +#define ISOLATION_LEVEL_SERIALIZABLE 3 + /* connection status */ #define CONN_STATUS_SETUP 0 #define CONN_STATUS_READY 1 diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index 41b73f19..70465132 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -35,15 +35,16 @@ #include /* Mapping from isolation level name to value exposed by Python. - * Only used for backward compatibility by the isolation_level property */ - + * + * Note: ordering matters: to get a valid pre-PG 8 level from one not valid, + * we increase a pointer in this list by one position. */ const IsolationLevel conn_isolevels[] = { - {"", 0}, /* autocommit */ - {"read committed", 1}, - {"read uncommitted", 1}, /* comes after to report real level */ - {"repeatable read", 2}, - {"serializable", 3}, - {"default", -1}, + {"", ISOLATION_LEVEL_AUTOCOMMIT}, + {"read uncommitted", ISOLATION_LEVEL_READ_UNCOMMITTED}, + {"read committed", ISOLATION_LEVEL_READ_COMMITTED}, + {"repeatable read", ISOLATION_LEVEL_REPEATABLE_READ}, + {"serializable", ISOLATION_LEVEL_SERIALIZABLE}, + {"default", -1}, /* never to be found on the server */ { NULL } }; @@ -1041,8 +1042,10 @@ conn_switch_isolation_level(connectionObject *self, int level) /* use only supported levels on older PG versions */ if (self->server_version < 80000) { - if (level == 2) - level = 3; + if (level == ISOLATION_LEVEL_READ_UNCOMMITTED) + level = ISOLATION_LEVEL_READ_COMMITTED; + else if (level == ISOLATION_LEVEL_REPEATABLE_READ) + level = ISOLATION_LEVEL_SERIALIZABLE; } if (-1 == (curr_level = conn_get_isolation_level(self))) { diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 02bc4da5..693de3ce 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -405,9 +405,9 @@ _psyco_conn_parse_isolevel(connectionObject *self, PyObject *pyval) if (PyInt_Check(pyval)) { long level = PyInt_AsLong(pyval); if (level == -1 && PyErr_Occurred()) { goto exit; } - if (level < 1 || level > 3) { + if (level < 1 || level > 4) { PyErr_SetString(PyExc_ValueError, - "isolation_level must be between 1 and 3"); + "isolation_level must be between 1 and 4"); goto exit; } @@ -437,7 +437,8 @@ _psyco_conn_parse_isolevel(connectionObject *self, PyObject *pyval) /* use only supported levels on older PG versions */ if (isolevel && self->server_version < 80000) { - if (isolevel->value == 1 || isolevel->value == 3) { + if (isolevel->value == ISOLATION_LEVEL_READ_UNCOMMITTED + || isolevel->value == ISOLATION_LEVEL_REPEATABLE_READ) { ++isolevel; } } diff --git a/tests/test_connection.py b/tests/test_connection.py index 0a791449..a887313a 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -206,6 +206,7 @@ class IsolationLevelsTestCase(unittest.TestCase): levels = [ (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), @@ -215,8 +216,10 @@ class IsolationLevelsTestCase(unittest.TestCase): # the only values available on prehistoric PG versions if conn.server_version < 80000: - if level == psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ: - name, level = ('serializable', psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + if level in ( + psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED, + psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ): + name, level = levels[levels.index((name, level)) + 1] self.assertEqual(conn.isolation_level, level) @@ -768,11 +771,13 @@ class TransactionControlTests(unittest.TestCase): self.assertEqual(cur.fetchone()[0], 'read committed') self.conn.rollback() - # 'read uncommitted' is internally translated to 'read committed' self.conn.set_session( isolation_level=psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED) cur.execute("SHOW default_transaction_isolation;") - self.assertEqual(cur.fetchone()[0], 'read committed') + if self.conn.server_version > 80000: + self.assertEqual(cur.fetchone()[0], 'read uncommitted') + else: + self.assertEqual(cur.fetchone()[0], 'read committed') self.conn.rollback() def test_set_isolation_level_str(self):