Set default_transaction_* GUC if session state is changed in autocomit

This commit is contained in:
Daniele Varrazzo 2017-02-04 15:19:41 +00:00
parent 665e9dc665
commit 9054eeccc0
4 changed files with 102 additions and 30 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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]);

View File

@ -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()