diff options
author | Daniele Varrazzo <daniele.varrazzo@gmail.com> | 2017-02-04 15:19:41 +0000 |
---|---|---|
committer | Daniele Varrazzo <daniele.varrazzo@gmail.com> | 2017-02-04 15:19:41 +0000 |
commit | 9054eeccc0460908865e5462973fd82c148a9315 (patch) | |
tree | 62a6c7f636b8c34344e2ede9486c36aba4ac82dc | |
parent | 665e9dc665cf72086c00871756c48e8d38bade2f (diff) | |
download | psycopg2-9054eeccc0460908865e5462973fd82c148a9315.tar.gz |
Set default_transaction_* GUC if session state is changed in autocomit
-rw-r--r-- | psycopg/connection_int.c | 75 | ||||
-rw-r--r-- | psycopg/connection_type.c | 2 | ||||
-rw-r--r-- | psycopg/pqpath.c | 6 | ||||
-rwxr-xr-x | tests/test_connection.py | 49 |
4 files changed, 102 insertions, 30 deletions
diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index 0cb4cf3..e6906eb 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -41,7 +41,7 @@ const char *srv_isolevels[] = { "REPEATABLE READ", "SERIALIZABLE", "READ UNCOMMITTED", - "" /* default */ + "default" /* only to set GUC, not for BEGIN */ }; /* Read only false, true */ @@ -58,6 +58,15 @@ const char *srv_deferrable[] = { "" /* default */ }; +/* On/Off/Default GUC states + */ +const char *srv_state_guc[] = { + "off", + "on", + "default" +}; + + /* Return a new "string" from a char* from the database. * * On Py2 just get a string, on Py3 decode it in the connection codec. @@ -1186,6 +1195,10 @@ RAISES_NEG int conn_set_session(connectionObject *self, int autocommit, int isolevel, int readonly, int deferrable) { + int rv = -1; + PGresult *pgres = NULL; + char *error = NULL; + /* Promote an isolation level to one of the levels supported by the server */ if (self->server_version < 80000) { if (isolevel == ISOLATION_LEVEL_READ_UNCOMMITTED) { @@ -1199,19 +1212,75 @@ conn_set_session(connectionObject *self, int autocommit, Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); + if (autocommit) { + /* we are in autocommit state, so no BEGIN will be issued: + * configure the session with the characteristics requested */ + if (isolevel != self->isolevel) { + if (0 > pq_set_guc_locked(self, + "default_transaction_isolation", srv_isolevels[isolevel], + &pgres, &error, &_save)) { + goto endlock; + } + } + if (readonly != self->readonly) { + if (0 > pq_set_guc_locked(self, + "default_transaction_read_only", srv_state_guc[readonly], + &pgres, &error, &_save)) { + goto endlock; + } + } + if (deferrable != self->deferrable && self->server_version >= 90100) { + if (0 > pq_set_guc_locked(self, + "default_transaction_deferrable", srv_state_guc[deferrable], + &pgres, &error, &_save)) { + goto endlock; + } + } + } + else if (self->autocommit) { + /* we are moving from autocommit to not autocommit, so revert the + * characteristics to defaults to let BEGIN do its work */ + if (0 > pq_set_guc_locked(self, + "default_transaction_isolation", "default", + &pgres, &error, &_save)) { + goto endlock; + } + if (0 > pq_set_guc_locked(self, + "default_transaction_read_only", "default", + &pgres, &error, &_save)) { + goto endlock; + } + if (self->server_version >= 90100) { + if (0 > pq_set_guc_locked(self, + "default_transaction_deferrable", "default", + &pgres, &error, &_save)) { + goto endlock; + } + } + } + + self->autocommit = autocommit; self->isolevel = isolevel; self->readonly = readonly; self->deferrable = deferrable; - self->autocommit = autocommit; + rv = 0; +endlock: pthread_mutex_unlock(&self->lock); Py_END_ALLOW_THREADS; + if (rv < 0) { + pq_complete_error(self, &pgres, &error); + goto exit; + } + Dprintf( "conn_set_session: autocommit %d, isolevel %d, readonly %d, deferrable %d", autocommit, isolevel, readonly, deferrable); - return 0; + +exit: + return rv; } diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 7b58e3f..7647056 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -671,7 +671,7 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "i", &level)) return NULL; - if (level < 0 || level > 4) { + if (level < 0 || level > 5) { PyErr_SetString(PyExc_ValueError, "isolation level must be between 0 and 4"); return NULL; diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c index 8a4d78f..c686402 100644 --- a/psycopg/pqpath.c +++ b/psycopg/pqpath.c @@ -495,8 +495,10 @@ pq_begin_locked(connectionObject *conn, PGresult **pgres, char **error, snprintf(buf, bufsize, "BEGIN%s%s%s%s%s", conn->server_version < 80000 ? ";SET TRANSACTION" : "", - (conn->isolevel >= 1 && conn->isolevel <= 4) ? " ISOLATION LEVEL " : "", - srv_isolevels[conn->isolevel], + (conn->isolevel >= 1 && conn->isolevel <= 4) + ? " ISOLATION LEVEL " : "", + (conn->isolevel >= 1 && conn->isolevel <= 4) + ? srv_isolevels[conn->isolevel] : "", srv_readonly[conn->readonly], srv_deferrable[conn->deferrable]); diff --git a/tests/test_connection.py b/tests/test_connection.py index 5b304ee..314aa07 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -529,7 +529,26 @@ class IsolationLevelsTestCase(ConnectingTestCase): conn.commit() self.assertRaises(ValueError, conn.set_isolation_level, -1) - self.assertRaises(ValueError, conn.set_isolation_level, 5) + self.assertRaises(ValueError, conn.set_isolation_level, 6) + + def test_set_isolation_level_default(self): + conn = self.connect() + curs = conn.cursor() + + conn.autocommit = True + curs.execute("set default_transaction_isolation to 'read committed'") + + conn.autocommit = False + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + self.assertEqual(conn.isolation_level, + psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + curs.execute("show transaction_isolation") + self.assertEqual(curs.fetchone()[0], "serializable") + + conn.rollback() + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_DEFAULT) + curs.execute("show transaction_isolation") + self.assertEqual(curs.fetchone()[0], "read committed") def test_set_isolation_level_abort(self): conn = self.connect() @@ -541,32 +560,14 @@ class IsolationLevelsTestCase(ConnectingTestCase): self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS, conn.get_transaction_status()) - conn.set_isolation_level( + # changed in psycopg 2.7 + self.assertRaises(psycopg2.ProgrammingError, + 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]) + self.assertEqual(conn.isolation_level, + psycopg2.extensions.ISOLATION_LEVEL_DEFAULT) def test_isolation_level_autocommit(self): cnn1 = self.connect() |