From c60682c230f8e9ef6a395be55409d549fa5d7239 Mon Sep 17 00:00:00 2001
From: Daniele Varrazzo <daniele.varrazzo@gmail.com>
Date: Sat, 4 Feb 2017 13:24:41 +0000
Subject: [PATCH] Reuse set_session to implement autocommit,
 set_isolation_level

---
 psycopg/connection.h      |  2 --
 psycopg/connection_int.c  | 66 ++++++++++-----------------------------
 psycopg/connection_type.c | 38 +++++++++++++++-------
 3 files changed, 43 insertions(+), 63 deletions(-)

diff --git a/psycopg/connection.h b/psycopg/connection.h
index 00430f95..65efcaf0 100644
--- a/psycopg/connection.h
+++ b/psycopg/connection.h
@@ -166,10 +166,8 @@ HIDDEN void conn_close(connectionObject *self);
 HIDDEN void conn_close_locked(connectionObject *self);
 RAISES_NEG HIDDEN int  conn_commit(connectionObject *self);
 RAISES_NEG HIDDEN int  conn_rollback(connectionObject *self);
-HIDDEN int  conn_set_autocommit(connectionObject *self, int value);
 RAISES_NEG HIDDEN int conn_set_session(connectionObject *self, int autocommit,
         int isolevel, int readonly, int deferrable);
-RAISES_NEG HIDDEN int  conn_switch_isolation_level(connectionObject *self, int level);
 RAISES_NEG HIDDEN int  conn_set_client_encoding(connectionObject *self, const char *enc);
 HIDDEN int  conn_poll(connectionObject *self);
 RAISES_NEG HIDDEN int  conn_tpc_begin(connectionObject *self, xidObject *xid);
diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c
index da80b3aa..0cb4cf38 100644
--- a/psycopg/connection_int.c
+++ b/psycopg/connection_int.c
@@ -1180,68 +1180,36 @@ conn_rollback(connectionObject *self)
     return res;
 }
 
-int
-conn_set_autocommit(connectionObject *self, int value)
-{
-    Py_BEGIN_ALLOW_THREADS;
-    pthread_mutex_lock(&self->lock);
-
-    self->autocommit = value;
-
-    pthread_mutex_unlock(&self->lock);
-    Py_END_ALLOW_THREADS;
-
-    return 0;
-}
-
-
-/* Promote an isolation level to one of the levels supported by the server */
-
-static int
-_adjust_isolevel(connectionObject *self, int level) {
-    if (self->server_version < 80000) {
-        if (level == ISOLATION_LEVEL_READ_UNCOMMITTED) {
-            level = ISOLATION_LEVEL_READ_COMMITTED;
-        }
-        else if (level == ISOLATION_LEVEL_REPEATABLE_READ) {
-            level = ISOLATION_LEVEL_SERIALIZABLE;
-        }
-    }
-    return level;
-}
-
 
 /* Change the state of the session */
 RAISES_NEG int
 conn_set_session(connectionObject *self, int autocommit,
         int isolevel, int readonly, int deferrable)
 {
-    isolevel = _adjust_isolevel(self, isolevel);
+    /* Promote an isolation level to one of the levels supported by the server */
+    if (self->server_version < 80000) {
+        if (isolevel == ISOLATION_LEVEL_READ_UNCOMMITTED) {
+            isolevel = ISOLATION_LEVEL_READ_COMMITTED;
+        }
+        else if (isolevel == ISOLATION_LEVEL_REPEATABLE_READ) {
+            isolevel = ISOLATION_LEVEL_SERIALIZABLE;
+        }
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    pthread_mutex_lock(&self->lock);
 
     self->isolevel = isolevel;
     self->readonly = readonly;
     self->deferrable = deferrable;
     self->autocommit = autocommit;
 
-    return 0;
-}
+    pthread_mutex_unlock(&self->lock);
+    Py_END_ALLOW_THREADS;
 
-
-/* conn_switch_isolation_level - switch isolation level on the connection */
-
-RAISES_NEG int
-conn_switch_isolation_level(connectionObject *self, int level)
-{
-    if (level == 0) {
-        self->autocommit = 1;
-    }
-    else {
-        level = _adjust_isolevel(self, level);
-        self->isolevel = level;
-        self->autocommit = 0;
-    }
-
-    Dprintf("conn_switch_isolation_level: switched to level %d", level);
+    Dprintf(
+        "conn_set_session: autocommit %d, isolevel %d, readonly %d, deferrable %d",
+        autocommit, isolevel, readonly, deferrable);
 
     return 0;
 }
diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c
index f574b090..7b58e3f7 100644
--- a/psycopg/connection_type.c
+++ b/psycopg/connection_type.c
@@ -531,6 +531,14 @@ exit:
     return rv;
 }
 
+#define _set_session_checks(self,what) \
+do { \
+    EXC_IF_CONN_CLOSED(self); \
+    EXC_IF_CONN_ASYNC(self, what); \
+    EXC_IF_IN_TRANSACTION(self, what); \
+    EXC_IF_TPC_PREPARED(self, what); \
+} while(0)
+
 /* set_session - set default transaction characteristics */
 
 #define psyco_conn_set_session_doc \
@@ -553,9 +561,7 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
     static char *kwlist[] =
         {"isolation_level", "readonly", "deferrable", "autocommit", NULL};
 
-    EXC_IF_CONN_CLOSED(self);
-    EXC_IF_CONN_ASYNC(self, set_session);
-    EXC_IF_IN_TRANSACTION(self, set_session);
+    _set_session_checks(self, set_session);
 
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO", kwlist,
             &isolevel, &readonly, &deferrable, &autocommit)) {
@@ -615,9 +621,7 @@ _psyco_conn_autocommit_set_checks(connectionObject *self)
 {
     /* wrapper to use the EXC_IF macros.
      * return NULL in case of error, else whatever */
-    EXC_IF_CONN_CLOSED(self);
-    EXC_IF_CONN_ASYNC(self, autocommit);
-    EXC_IF_IN_TRANSACTION(self, autocommit);
+    _set_session_checks(self, autocommit);
     return Py_None;     /* borrowed */
 }
 
@@ -628,7 +632,10 @@ psyco_conn_autocommit_set(connectionObject *self, PyObject *pyvalue)
 
     if (!_psyco_conn_autocommit_set_checks(self)) { return -1; }
     if (-1 == (value = PyObject_IsTrue(pyvalue))) { return -1; }
-    if (0 != conn_set_autocommit(self, value)) { return -1; }
+    if (0 > conn_set_session(self, value,
+                self->isolevel, self->readonly, self->deferrable)) {
+        return -1;
+    }
 
     return 0;
 }
@@ -660,9 +667,7 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
 {
     int level = 1;
 
-    EXC_IF_CONN_CLOSED(self);
-    EXC_IF_CONN_ASYNC(self, set_isolation_level);
-    EXC_IF_TPC_PREPARED(self, set_isolation_level);
+    _set_session_checks(self, set_isolation_level);
 
     if (!PyArg_ParseTuple(args, "i", &level)) return NULL;
 
@@ -672,8 +677,17 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
         return NULL;
     }
 
-    if (conn_switch_isolation_level(self, level) < 0) {
-        return NULL;
+    if (level == 0) {
+        if (0 > conn_set_session(self, 1,
+                ISOLATION_LEVEL_DEFAULT, self->readonly, self->deferrable)) {
+            return NULL;
+        }
+    }
+    else {
+        if (0 > conn_set_session(self, 0,
+                level, self->readonly, self->deferrable)) {
+            return NULL;
+        }
     }
 
     Py_RETURN_NONE;