summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>2017-02-04 15:19:41 +0000
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>2017-02-04 15:19:41 +0000
commit9054eeccc0460908865e5462973fd82c148a9315 (patch)
tree62a6c7f636b8c34344e2ede9486c36aba4ac82dc
parent665e9dc665cf72086c00871756c48e8d38bade2f (diff)
downloadpsycopg2-9054eeccc0460908865e5462973fd82c148a9315.tar.gz
Set default_transaction_* GUC if session state is changed in autocomit
-rw-r--r--psycopg/connection_int.c75
-rw-r--r--psycopg/connection_type.c2
-rw-r--r--psycopg/pqpath.c6
-rwxr-xr-xtests/test_connection.py49
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()