Merge branch 'fix-idempotence-check' into maint_2_7

This commit is contained in:
Daniele Varrazzo 2018-01-11 02:23:06 +00:00
commit ac114d2188
4 changed files with 48 additions and 22 deletions

2
NEWS
View File

@ -6,6 +6,8 @@ What's new in psycopg 2.7.4
- Fixed Solaris 10 support (:ticket:`#532`). - Fixed Solaris 10 support (:ticket:`#532`).
- `cursor.mogrify()` can be called on closed cursors (:ticket:`#579`). - `cursor.mogrify()` can be called on closed cursors (:ticket:`#579`).
- Fixed setting session characteristics in corner cases on autocommit
connections (:ticket:`#580`).
- Fixed `~psycopg2.extras.MinTimeLoggingCursor` on Python 3 (:ticket:`#609`). - Fixed `~psycopg2.extras.MinTimeLoggingCursor` on Python 3 (:ticket:`#609`).
- Fixed parsing of array of points as floats (:ticket:`#613`). - Fixed parsing of array of points as floats (:ticket:`#613`).
- Fixed `~psycopg2.__libpq_version__` building with libpq >= 10.1 - Fixed `~psycopg2.__libpq_version__` building with libpq >= 10.1

View File

@ -67,6 +67,9 @@ const char *srv_state_guc[] = {
}; };
const int SRV_STATE_UNCHANGED = -1;
/* Return a new "string" from a char* from the database. /* Return a new "string" from a char* from the database.
* *
* On Py2 just get a string, on Py3 decode it in the connection codec. * On Py2 just get a string, on Py3 decode it in the connection codec.
@ -1188,6 +1191,8 @@ conn_set_session(connectionObject *self, int autocommit,
int rv = -1; int rv = -1;
PGresult *pgres = NULL; PGresult *pgres = NULL;
char *error = NULL; char *error = NULL;
int want_autocommit = autocommit == SRV_STATE_UNCHANGED ?
self->autocommit : autocommit;
if (deferrable != self->deferrable && self->server_version < 90100) { if (deferrable != self->deferrable && self->server_version < 90100) {
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
@ -1209,24 +1214,24 @@ conn_set_session(connectionObject *self, int autocommit,
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock); pthread_mutex_lock(&self->lock);
if (autocommit) { if (want_autocommit) {
/* we are in autocommit state, so no BEGIN will be issued: /* we are or are going in autocommit state, so no BEGIN will be issued:
* configure the session with the characteristics requested */ * configure the session with the characteristics requested */
if (isolevel != self->isolevel) { if (isolevel != SRV_STATE_UNCHANGED) {
if (0 > pq_set_guc_locked(self, if (0 > pq_set_guc_locked(self,
"default_transaction_isolation", srv_isolevels[isolevel], "default_transaction_isolation", srv_isolevels[isolevel],
&pgres, &error, &_save)) { &pgres, &error, &_save)) {
goto endlock; goto endlock;
} }
} }
if (readonly != self->readonly) { if (readonly != SRV_STATE_UNCHANGED) {
if (0 > pq_set_guc_locked(self, if (0 > pq_set_guc_locked(self,
"default_transaction_read_only", srv_state_guc[readonly], "default_transaction_read_only", srv_state_guc[readonly],
&pgres, &error, &_save)) { &pgres, &error, &_save)) {
goto endlock; goto endlock;
} }
} }
if (deferrable != self->deferrable) { if (deferrable != SRV_STATE_UNCHANGED) {
if (0 > pq_set_guc_locked(self, if (0 > pq_set_guc_locked(self,
"default_transaction_deferrable", srv_state_guc[deferrable], "default_transaction_deferrable", srv_state_guc[deferrable],
&pgres, &error, &_save)) { &pgres, &error, &_save)) {
@ -1260,10 +1265,18 @@ conn_set_session(connectionObject *self, int autocommit,
} }
} }
self->autocommit = autocommit; if (autocommit != SRV_STATE_UNCHANGED) {
self->isolevel = isolevel; self->autocommit = autocommit;
self->readonly = readonly; }
self->deferrable = deferrable; if (isolevel != SRV_STATE_UNCHANGED) {
self->isolevel = isolevel;
}
if (readonly != SRV_STATE_UNCHANGED) {
self->readonly = readonly;
}
if (deferrable != SRV_STATE_UNCHANGED) {
self->deferrable = deferrable;
}
rv = 0; rv = 0;
endlock: endlock:

View File

@ -39,6 +39,7 @@
extern HIDDEN const char *srv_isolevels[]; extern HIDDEN const char *srv_isolevels[];
extern HIDDEN const char *srv_readonly[]; extern HIDDEN const char *srv_readonly[];
extern HIDDEN const char *srv_deferrable[]; extern HIDDEN const char *srv_deferrable[];
extern HIDDEN const int SRV_STATE_UNCHANGED;
/** DBAPI methods **/ /** DBAPI methods **/
@ -561,10 +562,10 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
PyObject *deferrable = Py_None; PyObject *deferrable = Py_None;
PyObject *autocommit = Py_None; PyObject *autocommit = Py_None;
int c_isolevel = self->isolevel; int c_isolevel = SRV_STATE_UNCHANGED;
int c_readonly = self->readonly; int c_readonly = SRV_STATE_UNCHANGED;
int c_deferrable = self->deferrable; int c_deferrable = SRV_STATE_UNCHANGED;
int c_autocommit = self->autocommit; int c_autocommit = SRV_STATE_UNCHANGED;
static char *kwlist[] = static char *kwlist[] =
{"isolation_level", "readonly", "deferrable", "autocommit", NULL}; {"isolation_level", "readonly", "deferrable", "autocommit", NULL};
@ -637,7 +638,7 @@ psyco_conn_autocommit_set(connectionObject *self, PyObject *pyvalue)
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; } if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
if (-1 == (value = PyObject_IsTrue(pyvalue))) { return -1; } if (-1 == (value = PyObject_IsTrue(pyvalue))) { return -1; }
if (0 > conn_set_session(self, value, if (0 > conn_set_session(self, value,
self->isolevel, self->readonly, self->deferrable)) { SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
return -1; return -1;
} }
@ -668,8 +669,8 @@ psyco_conn_isolation_level_set(connectionObject *self, PyObject *pyvalue)
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; } if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
if (0 > (value = _psyco_conn_parse_isolevel(pyvalue))) { return -1; } if (0 > (value = _psyco_conn_parse_isolevel(pyvalue))) { return -1; }
if (0 > conn_set_session(self, self->autocommit, if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
value, self->readonly, self->deferrable)) { value, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
return -1; return -1;
} }
@ -715,13 +716,13 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
if (level == 0) { if (level == 0) {
if (0 > conn_set_session(self, 1, if (0 > conn_set_session(self, 1,
self->isolevel, self->readonly, self->deferrable)) { SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
return NULL; return NULL;
} }
} }
else { else {
if (0 > conn_set_session(self, 0, if (0 > conn_set_session(self, 0,
level, self->readonly, self->deferrable)) { level, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
return NULL; return NULL;
} }
} }
@ -767,8 +768,8 @@ psyco_conn_readonly_set(connectionObject *self, PyObject *pyvalue)
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; } if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; } if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
if (0 > conn_set_session(self, self->autocommit, if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
self->isolevel, value, self->deferrable)) { SRV_STATE_UNCHANGED, value, SRV_STATE_UNCHANGED)) {
return -1; return -1;
} }
@ -813,8 +814,8 @@ psyco_conn_deferrable_set(connectionObject *self, PyObject *pyvalue)
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; } if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; } if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
if (0 > conn_set_session(self, self->autocommit, if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
self->isolevel, self->readonly, value)) { SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, value)) {
return -1; return -1;
} }

View File

@ -1382,6 +1382,16 @@ class TransactionControlTests(ConnectingTestCase):
cur.execute("SHOW default_transaction_read_only;") cur.execute("SHOW default_transaction_read_only;")
self.assertEqual(cur.fetchone()[0], 'off') self.assertEqual(cur.fetchone()[0], 'off')
def test_idempotence_check(self):
self.conn.autocommit = False
self.conn.readonly = True
self.conn.autocommit = True
self.conn.readonly = True
cur = self.conn.cursor()
cur.execute("SHOW transaction_read_only")
self.assertEqual(cur.fetchone()[0], 'on')
class AutocommitTests(ConnectingTestCase): class AutocommitTests(ConnectingTestCase):
def test_closed(self): def test_closed(self):