diff options
author | Daniele Varrazzo <daniele.varrazzo@gmail.com> | 2017-02-04 02:25:48 +0000 |
---|---|---|
committer | Daniele Varrazzo <daniele.varrazzo@gmail.com> | 2017-02-04 02:38:56 +0000 |
commit | c1e016e597e1ee2b52e5277a09793caa9401f39d (patch) | |
tree | 3719e64a5efcdbe66652e19a4e38d531ef2ba479 /psycopg/connection_int.c | |
parent | 9863637f309e1e89523007c40e9dcdde69f60bc0 (diff) | |
download | psycopg2-c1e016e597e1ee2b52e5277a09793caa9401f39d.tar.gz |
Don't use default_transaction_* for session characteristics
Store the state in the connection object and set the params on BEGIN
Some tests fail: a few can be fixed reading transaction_* instead of
default_transaction_*; but the behaviour of tx characteristics with
autocommit is effectively changed. It may be addressed by setting
default_transaction_* if autocommit is set.
Diffstat (limited to 'psycopg/connection_int.c')
-rw-r--r-- | psycopg/connection_int.c | 280 |
1 files changed, 118 insertions, 162 deletions
diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index f92a658..d8d693f 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -34,20 +34,29 @@ #include <string.h> -/* Mapping from isolation level name to value exposed by Python. - * - * Note: ordering matters: to get a valid pre-PG 8 level from one not valid, - * we increase a pointer in this list by one position. */ -const IsolationLevel conn_isolevels[] = { - {"", ISOLATION_LEVEL_AUTOCOMMIT}, - {"read uncommitted", ISOLATION_LEVEL_READ_UNCOMMITTED}, - {"read committed", ISOLATION_LEVEL_READ_COMMITTED}, - {"repeatable read", ISOLATION_LEVEL_REPEATABLE_READ}, - {"serializable", ISOLATION_LEVEL_SERIALIZABLE}, - {"default", -1}, /* never to be found on the server */ - { NULL } +/* String indexes match the ISOLATION_LEVEL_* consts */ +const char *srv_isolevels[] = { + NULL, /* autocommit */ + "READ COMMITTED", + "REPEATABLE READ", + "SERIALIZABLE", + "READ UNCOMMITTED", + "" /* default */ }; +/* Read only false, true */ +const char *srv_readonly[] = { + " READ WRITE", + " READ ONLY", + "" /* default */ +}; + +/* Deferrable false, true */ +const char *srv_deferrable[] = { + " NOT DEFERRABLE", + " DEFERRABLE", + "" /* default */ +}; /* Return a new "string" from a char* from the database. * @@ -553,50 +562,13 @@ exit: RAISES_NEG int conn_get_isolation_level(connectionObject *self) { - PGresult *pgres = NULL; - char *error = NULL; - int rv = -1; - char *lname; - const IsolationLevel *level; - /* this may get called by async connections too: here's your result */ if (self->autocommit) { - return 0; - } - - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&self->lock); - - if (!(lname = pq_get_guc_locked(self, "default_transaction_isolation", - &pgres, &error, &_save))) { - goto endlock; - } - - /* find the value for the requested isolation level */ - level = conn_isolevels; - while ((++level)->name) { - if (0 == strcasecmp(level->name, lname)) { - rv = level->value; - break; - } - } - if (-1 == rv) { - error = malloc(256); - PyOS_snprintf(error, 256, - "unexpected isolation level: '%s'", lname); + return ISOLATION_LEVEL_AUTOCOMMIT; } - - free(lname); - -endlock: - pthread_mutex_unlock(&self->lock); - Py_END_ALLOW_THREADS; - - if (rv < 0) { - pq_complete_error(self, &pgres, &error); + else { + return self->isolevel; } - - return rv; } @@ -1208,156 +1180,140 @@ conn_rollback(connectionObject *self) return res; } -RAISES_NEG int -conn_set_session(connectionObject *self, - const char *isolevel, const char *readonly, const char *deferrable, - int autocommit) +int +conn_set_autocommit(connectionObject *self, int value) { - PGresult *pgres = NULL; - char *error = NULL; - int res = -1; - Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); - if (isolevel) { - Dprintf("conn_set_session: setting isolation to %s", isolevel); - if ((res = pq_set_guc_locked(self, - "default_transaction_isolation", isolevel, - &pgres, &error, &_save))) { - goto endlock; - } - } + self->autocommit = value; - if (readonly) { - Dprintf("conn_set_session: setting read only to %s", readonly); - if ((res = pq_set_guc_locked(self, - "default_transaction_read_only", readonly, - &pgres, &error, &_save))) { - goto endlock; - } - } + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + return 0; +} - if (deferrable) { - Dprintf("conn_set_session: setting deferrable to %s", deferrable); - if ((res = pq_set_guc_locked(self, - "default_transaction_deferrable", deferrable, - &pgres, &error, &_save))) { - goto endlock; +/* 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; +} - if (self->autocommit != autocommit) { - Dprintf("conn_set_session: setting autocommit to %d", autocommit); - self->autocommit = autocommit; - } - res = 0; +/* parse a python object into one of the possible isolation level values */ -endlock: - pthread_mutex_unlock(&self->lock); - Py_END_ALLOW_THREADS; +RAISES_NEG int +conn_parse_isolevel(connectionObject *self, PyObject *pyval) +{ + int rv = -1; + long level; - if (res < 0) { - pq_complete_error(self, &pgres, &error); + Py_INCREF(pyval); /* for ensure_bytes */ + + /* parse from one of the level constants */ + if (PyInt_Check(pyval)) { + level = PyInt_AsLong(pyval); + if (level == -1 && PyErr_Occurred()) { goto exit; } + if (level < 1 || level > 4) { + PyErr_SetString(PyExc_ValueError, + "isolation_level must be between 1 and 4"); + goto exit; + } + + rv = level; } - return res; -} + /* parse from the string -- this includes "default" */ -int -conn_set_autocommit(connectionObject *self, int value) -{ - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&self->lock); + else { + if (!(pyval = psycopg_ensure_bytes(pyval))) { + goto exit; + } + for (level = 1; level <= 4; level++) { + if (0 == strcasecmp(srv_isolevels[level], Bytes_AS_STRING(pyval))) { + rv = level; + break; + } + } + if (rv < 0 && 0 == strcasecmp("default", Bytes_AS_STRING(pyval))) { + rv = ISOLATION_LEVEL_DEFAULT; + } + if (rv < 0) { + PyErr_Format(PyExc_ValueError, + "bad value for isolation_level: '%s'", Bytes_AS_STRING(pyval)); + goto exit; + } + } - self->autocommit = value; + rv = _adjust_isolevel(self, rv); - pthread_mutex_unlock(&self->lock); - Py_END_ALLOW_THREADS; +exit: + Py_XDECREF(pyval); - return 0; + return rv; } -/* conn_switch_isolation_level - switch isolation level on the connection */ +/* convert False/True/"default" -> 0/1/2 */ RAISES_NEG int -conn_switch_isolation_level(connectionObject *self, int level) +conn_parse_onoff(PyObject *pyval) { - PGresult *pgres = NULL; - char *error = NULL; - int curr_level; - int ret = -1; + int rv = -1; - /* use only supported levels on older PG versions */ - 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; - } + Py_INCREF(pyval); /* for ensure_bytes */ - if (-1 == (curr_level = conn_get_isolation_level(self))) { - return -1; + if (PyUnicode_CheckExact(pyval) || Bytes_CheckExact(pyval)) { + if (!(pyval = psycopg_ensure_bytes(pyval))) { + goto exit; + } + if (0 == strcasecmp("default", Bytes_AS_STRING(pyval))) { + rv = STATE_DEFAULT; + } + else { + PyErr_Format(PyExc_ValueError, + "the only string accepted is 'default'; got %s", + Bytes_AS_STRING(pyval)); + goto exit; + } } - - if (curr_level == level) { - /* no need to change level */ - return 0; + else { + int istrue; + if (0 > (istrue = PyObject_IsTrue(pyval))) { goto exit; } + rv = istrue ? STATE_ON : STATE_OFF; } - /* Emulate the previous semantic of set_isolation_level() using the - * functions currently available. */ +exit: + Py_XDECREF(pyval); - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&self->lock); + return rv; +} - /* terminate the current transaction if any */ - if ((ret = pq_abort_locked(self, &pgres, &error, &_save))) { - goto endlock; - } +/* conn_switch_isolation_level - switch isolation level on the connection */ +RAISES_NEG int +conn_switch_isolation_level(connectionObject *self, int level) +{ if (level == 0) { - if ((ret = pq_set_guc_locked(self, - "default_transaction_isolation", "default", - &pgres, &error, &_save))) { - goto endlock; - } self->autocommit = 1; } else { - /* find the name of the requested level */ - const IsolationLevel *isolevel = conn_isolevels; - while ((++isolevel)->name) { - if (level == isolevel->value) { - break; - } - } - if (!isolevel->name) { - ret = -1; - error = strdup("bad isolation level value"); - goto endlock; - } - - if ((ret = pq_set_guc_locked(self, - "default_transaction_isolation", isolevel->name, - &pgres, &error, &_save))) { - goto endlock; - } + level = _adjust_isolevel(self, level); + self->isolevel = level; self->autocommit = 0; } Dprintf("conn_switch_isolation_level: switched to level %d", level); -endlock: - pthread_mutex_unlock(&self->lock); - Py_END_ALLOW_THREADS; - - if (ret < 0) { - pq_complete_error(self, &pgres, &error); - } - - return ret; + return 0; } |