mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 18:33:44 +03:00
Added readonly and deferrable attributes
This commit is contained in:
parent
b5d80b609d
commit
d50ed48807
4
NEWS
4
NEWS
|
@ -39,7 +39,9 @@ New features:
|
||||||
control the session characteristics as it may create problems with external
|
control the session characteristics as it may create problems with external
|
||||||
connection pools such as pgbouncer; use :sql:`BEGIN` options instead
|
connection pools such as pgbouncer; use :sql:`BEGIN` options instead
|
||||||
(:ticket:`#503`).
|
(:ticket:`#503`).
|
||||||
- `~connection.isolation_level` is now writable.
|
- `~connection.isolation_level` is now writable and entirely separated from
|
||||||
|
`~connection.autocommit`; added `~connection.readonly`,
|
||||||
|
`connection.deferrable` writable attributes.
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
|
|
|
@ -386,12 +386,6 @@ The ``connection`` class
|
||||||
|
|
||||||
The function must be invoked with no transaction in progress.
|
The function must be invoked with no transaction in progress.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
There is currently no builtin method to read the current value for
|
|
||||||
the parameters: use :sql:`SHOW default_transaction_...` to read
|
|
||||||
the values from the backend.
|
|
||||||
|
|
||||||
.. seealso:: |SET TRANSACTION|_ for further details about the behaviour
|
.. seealso:: |SET TRANSACTION|_ for further details about the behaviour
|
||||||
of the transaction parameters in the server.
|
of the transaction parameters in the server.
|
||||||
|
|
||||||
|
@ -475,6 +469,35 @@ The ``connection`` class
|
||||||
transaction_isolation`. Usually the default value is `READ
|
transaction_isolation`. Usually the default value is `READ
|
||||||
COMMITTED`, but this may be changed in the server configuration.
|
COMMITTED`, but this may be changed in the server configuration.
|
||||||
|
|
||||||
|
This value is now entirely separate from the `autocommit`
|
||||||
|
property: in previous version, if `!autocommit` was set to `!True`
|
||||||
|
this property would have returned
|
||||||
|
`~psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT`; it will now
|
||||||
|
return the server isolation level.
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: readonly
|
||||||
|
|
||||||
|
Return or set the read-only status for the current session. Available
|
||||||
|
values are `!True` (new transactions will be in read-only mode),
|
||||||
|
`!False` (new transactions will be writable), `!None` (use the default
|
||||||
|
configured for the server by :sql:`default_transaction_read_only`).
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: deferrable
|
||||||
|
|
||||||
|
Return or set the `deferrable status`__ for the current session.
|
||||||
|
Available values are `!True` (new transactions will be in deferrable
|
||||||
|
mode), `!False` (new transactions will be in non deferrable mode),
|
||||||
|
`!None` (use the default configured for the server by
|
||||||
|
:sql:`default_transaction_deferrable`).
|
||||||
|
|
||||||
|
.. __: `SET TRANSACTION`_
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
|
||||||
.. method:: set_isolation_level(level)
|
.. method:: set_isolation_level(level)
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,6 @@ HIDDEN PyObject *conn_encode(connectionObject *self, PyObject *b);
|
||||||
HIDDEN PyObject *conn_decode(connectionObject *self, const char *str, Py_ssize_t len);
|
HIDDEN PyObject *conn_decode(connectionObject *self, const char *str, Py_ssize_t len);
|
||||||
HIDDEN int conn_get_standard_conforming_strings(PGconn *pgconn);
|
HIDDEN int conn_get_standard_conforming_strings(PGconn *pgconn);
|
||||||
HIDDEN PyObject *conn_pgenc_to_pyenc(const char *encoding, char **clean_encoding);
|
HIDDEN PyObject *conn_pgenc_to_pyenc(const char *encoding, char **clean_encoding);
|
||||||
RAISES_NEG HIDDEN int conn_get_isolation_level(connectionObject *self);
|
|
||||||
HIDDEN int conn_get_protocol_version(PGconn *pgconn);
|
HIDDEN int conn_get_protocol_version(PGconn *pgconn);
|
||||||
HIDDEN int conn_get_server_version(PGconn *pgconn);
|
HIDDEN int conn_get_server_version(PGconn *pgconn);
|
||||||
HIDDEN void conn_notice_process(connectionObject *self);
|
HIDDEN void conn_notice_process(connectionObject *self);
|
||||||
|
|
|
@ -568,19 +568,6 @@ exit:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RAISES_NEG int
|
|
||||||
conn_get_isolation_level(connectionObject *self)
|
|
||||||
{
|
|
||||||
/* this may get called by async connections too: here's your result */
|
|
||||||
if (self->autocommit) {
|
|
||||||
return ISOLATION_LEVEL_AUTOCOMMIT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return self->isolevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
conn_get_protocol_version(PGconn *pgconn)
|
conn_get_protocol_version(PGconn *pgconn)
|
||||||
{
|
{
|
||||||
|
@ -697,6 +684,9 @@ conn_setup(connectionObject *self, PGconn *pgconn)
|
||||||
|
|
||||||
/* for reset */
|
/* for reset */
|
||||||
self->autocommit = 0;
|
self->autocommit = 0;
|
||||||
|
self->isolevel = ISOLATION_LEVEL_DEFAULT;
|
||||||
|
self->readonly = STATE_DEFAULT;
|
||||||
|
self->deferrable = STATE_DEFAULT;
|
||||||
|
|
||||||
/* success */
|
/* success */
|
||||||
rv = 0;
|
rv = 0;
|
||||||
|
|
|
@ -510,7 +510,10 @@ _psyco_conn_parse_onoff(PyObject *pyval)
|
||||||
|
|
||||||
Py_INCREF(pyval); /* for ensure_bytes */
|
Py_INCREF(pyval); /* for ensure_bytes */
|
||||||
|
|
||||||
if (PyUnicode_CheckExact(pyval) || Bytes_CheckExact(pyval)) {
|
if (pyval == Py_None) {
|
||||||
|
rv = STATE_DEFAULT;
|
||||||
|
}
|
||||||
|
else if (PyUnicode_CheckExact(pyval) || Bytes_CheckExact(pyval)) {
|
||||||
if (!(pyval = psycopg_ensure_bytes(pyval))) {
|
if (!(pyval = psycopg_ensure_bytes(pyval))) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
@ -591,7 +594,7 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
|
||||||
" from PostgreSQL 9.1");
|
" from PostgreSQL 9.1");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (0 > (c_deferrable = _psyco_conn_parse_onoff(readonly))) {
|
if (0 > (c_deferrable = _psyco_conn_parse_onoff(deferrable))) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -609,6 +612,8 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* autocommit - return or set the current autocommit status */
|
||||||
|
|
||||||
#define psyco_conn_autocommit_doc \
|
#define psyco_conn_autocommit_doc \
|
||||||
"Set or return the autocommit status."
|
"Set or return the autocommit status."
|
||||||
|
|
||||||
|
@ -646,7 +651,7 @@ psyco_conn_autocommit_set(connectionObject *self, PyObject *pyvalue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* isolation_level - return the current isolation level */
|
/* isolation_level - return or set the current isolation level */
|
||||||
|
|
||||||
#define psyco_conn_isolation_level_doc \
|
#define psyco_conn_isolation_level_doc \
|
||||||
"Set or return the connection transaction isolation level."
|
"Set or return the connection transaction isolation level."
|
||||||
|
@ -654,23 +659,14 @@ psyco_conn_autocommit_set(connectionObject *self, PyObject *pyvalue)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
psyco_conn_isolation_level_get(connectionObject *self)
|
psyco_conn_isolation_level_get(connectionObject *self)
|
||||||
{
|
{
|
||||||
int rv;
|
if (self->isolevel == ISOLATION_LEVEL_DEFAULT) {
|
||||||
|
|
||||||
EXC_IF_CONN_CLOSED(self);
|
|
||||||
EXC_IF_TPC_PREPARED(self, set_isolation_level);
|
|
||||||
|
|
||||||
rv = conn_get_isolation_level(self);
|
|
||||||
if (-1 == rv) { return NULL; }
|
|
||||||
if (ISOLATION_LEVEL_DEFAULT == rv) {
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
} else {
|
} else {
|
||||||
return PyInt_FromLong((long)rv);
|
return PyInt_FromLong((long)self->isolevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* isolation_level - set a new isolation level */
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
psyco_conn_isolation_level_set(connectionObject *self, PyObject *pyvalue)
|
psyco_conn_isolation_level_set(connectionObject *self, PyObject *pyvalue)
|
||||||
{
|
{
|
||||||
|
@ -725,7 +721,7 @@ 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,
|
||||||
ISOLATION_LEVEL_DEFAULT, self->readonly, self->deferrable)) {
|
self->isolevel, self->readonly, self->deferrable)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -739,6 +735,99 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* readonly - return or set the current read-only status */
|
||||||
|
|
||||||
|
#define psyco_conn_readonly_doc \
|
||||||
|
"Set or return the connection read-only status."
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
psyco_conn_readonly_get(connectionObject *self)
|
||||||
|
{
|
||||||
|
PyObject *rv = NULL;
|
||||||
|
|
||||||
|
switch (self->readonly) {
|
||||||
|
case STATE_OFF:
|
||||||
|
rv = Py_False;
|
||||||
|
break;
|
||||||
|
case STATE_ON:
|
||||||
|
rv = Py_True;
|
||||||
|
break;
|
||||||
|
case STATE_DEFAULT:
|
||||||
|
rv = Py_None;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_Format(InternalError,
|
||||||
|
"bad internal value for readonly: %d", self->readonly);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
psyco_conn_readonly_set(connectionObject *self, PyObject *pyvalue)
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
|
||||||
|
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
||||||
|
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
|
||||||
|
if (0 > conn_set_session(self, self->autocommit,
|
||||||
|
self->isolevel, value, self->deferrable)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* deferrable - return or set the current deferrable status */
|
||||||
|
|
||||||
|
#define psyco_conn_deferrable_doc \
|
||||||
|
"Set or return the connection deferrable status."
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
psyco_conn_deferrable_get(connectionObject *self)
|
||||||
|
{
|
||||||
|
PyObject *rv = NULL;
|
||||||
|
|
||||||
|
switch (self->deferrable) {
|
||||||
|
case STATE_OFF:
|
||||||
|
rv = Py_False;
|
||||||
|
break;
|
||||||
|
case STATE_ON:
|
||||||
|
rv = Py_True;
|
||||||
|
break;
|
||||||
|
case STATE_DEFAULT:
|
||||||
|
rv = Py_None;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_Format(InternalError,
|
||||||
|
"bad internal value for deferrable: %d", self->deferrable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
psyco_conn_deferrable_set(connectionObject *self, PyObject *pyvalue)
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
|
||||||
|
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
||||||
|
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
|
||||||
|
if (0 > conn_set_session(self, self->autocommit,
|
||||||
|
self->isolevel, self->readonly, value)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* set_client_encoding method - set client encoding */
|
/* set_client_encoding method - set client encoding */
|
||||||
|
|
||||||
#define psyco_conn_set_client_encoding_doc \
|
#define psyco_conn_set_client_encoding_doc \
|
||||||
|
@ -1151,6 +1240,14 @@ static struct PyGetSetDef connectionObject_getsets[] = {
|
||||||
(getter)psyco_conn_isolation_level_get,
|
(getter)psyco_conn_isolation_level_get,
|
||||||
(setter)psyco_conn_isolation_level_set,
|
(setter)psyco_conn_isolation_level_set,
|
||||||
psyco_conn_isolation_level_doc },
|
psyco_conn_isolation_level_doc },
|
||||||
|
{ "readonly",
|
||||||
|
(getter)psyco_conn_readonly_get,
|
||||||
|
(setter)psyco_conn_readonly_set,
|
||||||
|
psyco_conn_readonly_doc },
|
||||||
|
{ "deferrable",
|
||||||
|
(getter)psyco_conn_deferrable_get,
|
||||||
|
(setter)psyco_conn_deferrable_set,
|
||||||
|
psyco_conn_deferrable_doc },
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
#undef EXCEPTION_GETTER
|
#undef EXCEPTION_GETTER
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
from testutils import unittest, skip_before_postgres, slow
|
from testutils import unittest, skip_before_postgres, slow
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2 import extensions
|
from psycopg2 import extensions as ext
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import StringIO
|
import StringIO
|
||||||
|
@ -74,13 +74,14 @@ class AsyncTests(ConnectingTestCase):
|
||||||
self.assert_(self.conn.async_)
|
self.assert_(self.conn.async_)
|
||||||
self.assert_(not self.sync_conn.async_)
|
self.assert_(not self.sync_conn.async_)
|
||||||
|
|
||||||
# the async connection should be in isolevel 0
|
# the async connection should be autocommit
|
||||||
self.assertEquals(self.conn.isolation_level, 0)
|
self.assert_(self.conn.autocommit)
|
||||||
|
self.assertEquals(self.conn.isolation_level, ext.ISOLATION_LEVEL_DEFAULT)
|
||||||
|
|
||||||
# check other properties to be found on the connection
|
# check other properties to be found on the connection
|
||||||
self.assert_(self.conn.server_version)
|
self.assert_(self.conn.server_version)
|
||||||
self.assert_(self.conn.protocol_version in (2, 3))
|
self.assert_(self.conn.protocol_version in (2, 3))
|
||||||
self.assert_(self.conn.encoding in psycopg2.extensions.encodings)
|
self.assert_(self.conn.encoding in ext.encodings)
|
||||||
|
|
||||||
def test_async_named_cursor(self):
|
def test_async_named_cursor(self):
|
||||||
self.assertRaises(psycopg2.ProgrammingError,
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
@ -192,7 +193,7 @@ class AsyncTests(ConnectingTestCase):
|
||||||
|
|
||||||
# getting transaction status works
|
# getting transaction status works
|
||||||
self.assertEquals(self.conn.get_transaction_status(),
|
self.assertEquals(self.conn.get_transaction_status(),
|
||||||
extensions.TRANSACTION_STATUS_ACTIVE)
|
ext.TRANSACTION_STATUS_ACTIVE)
|
||||||
self.assertTrue(self.conn.isexecuting())
|
self.assertTrue(self.conn.isexecuting())
|
||||||
|
|
||||||
# setting connection encoding should fail
|
# setting connection encoding should fail
|
||||||
|
@ -311,9 +312,9 @@ class AsyncTests(ConnectingTestCase):
|
||||||
self.assertEquals(cur.fetchone()[0], "b" * 10000)
|
self.assertEquals(cur.fetchone()[0], "b" * 10000)
|
||||||
|
|
||||||
def test_async_subclass(self):
|
def test_async_subclass(self):
|
||||||
class MyConn(psycopg2.extensions.connection):
|
class MyConn(ext.connection):
|
||||||
def __init__(self, dsn, async_=0):
|
def __init__(self, dsn, async_=0):
|
||||||
psycopg2.extensions.connection.__init__(self, dsn, async_=async_)
|
ext.connection.__init__(self, dsn, async_=async_)
|
||||||
|
|
||||||
conn = self.connect(connection_factory=MyConn, async_=True)
|
conn = self.connect(connection_factory=MyConn, async_=True)
|
||||||
self.assert_(isinstance(conn, MyConn))
|
self.assert_(isinstance(conn, MyConn))
|
||||||
|
@ -330,7 +331,7 @@ class AsyncTests(ConnectingTestCase):
|
||||||
curs.execute("select %s;", ('x' * size,))
|
curs.execute("select %s;", ('x' * size,))
|
||||||
self.wait(stub)
|
self.wait(stub)
|
||||||
self.assertEqual(size, len(curs.fetchone()[0]))
|
self.assertEqual(size, len(curs.fetchone()[0]))
|
||||||
if stub.polls.count(psycopg2.extensions.POLL_WRITE) > 1:
|
if stub.polls.count(ext.POLL_WRITE) > 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
# This is more a testing glitch than an error: it happens
|
# This is more a testing glitch than an error: it happens
|
||||||
|
|
|
@ -59,8 +59,8 @@ class AsyncTests(ConnectingTestCase):
|
||||||
self.assert_(self.conn.async)
|
self.assert_(self.conn.async)
|
||||||
self.assert_(not self.sync_conn.async)
|
self.assert_(not self.sync_conn.async)
|
||||||
|
|
||||||
# the async connection should be in isolevel 0
|
# the async connection should be autocommit
|
||||||
self.assertEquals(self.conn.isolation_level, 0)
|
self.assert_(self.conn.autocommit)
|
||||||
|
|
||||||
# check other properties to be found on the connection
|
# check other properties to be found on the connection
|
||||||
self.assert_(self.conn.server_version)
|
self.assert_(self.conn.server_version)
|
||||||
|
|
|
@ -89,13 +89,26 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
|
|
||||||
def test_reset(self):
|
def test_reset(self):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
# switch isolation level, then reset
|
# switch session characteristics
|
||||||
level = conn.isolation_level
|
conn.autocommit = True
|
||||||
conn.set_isolation_level(0)
|
conn.isolation_level = 'serializable'
|
||||||
self.assertEqual(conn.isolation_level, 0)
|
conn.readonly = True
|
||||||
|
if self.conn.server_version >= 90100:
|
||||||
|
conn.deferrable = False
|
||||||
|
|
||||||
|
self.assert_(conn.autocommit)
|
||||||
|
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_SERIALIZABLE)
|
||||||
|
self.assert_(conn.readonly is True)
|
||||||
|
if self.conn.server_version >= 90100:
|
||||||
|
self.assert_(conn.deferrable is False)
|
||||||
|
|
||||||
conn.reset()
|
conn.reset()
|
||||||
# now the isolation level should be equal to saved one
|
# now the session characteristics should be reverted
|
||||||
self.assertEqual(conn.isolation_level, level)
|
self.assert_(not conn.autocommit)
|
||||||
|
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_DEFAULT)
|
||||||
|
self.assert_(conn.readonly is None)
|
||||||
|
if self.conn.server_version >= 90100:
|
||||||
|
self.assert_(conn.deferrable is None)
|
||||||
|
|
||||||
def test_notices(self):
|
def test_notices(self):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
|
@ -499,7 +512,6 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
|
|
||||||
levels = [
|
levels = [
|
||||||
(None, ext.ISOLATION_LEVEL_AUTOCOMMIT),
|
|
||||||
('read uncommitted',
|
('read uncommitted',
|
||||||
ext.ISOLATION_LEVEL_READ_UNCOMMITTED),
|
ext.ISOLATION_LEVEL_READ_UNCOMMITTED),
|
||||||
('read committed', ext.ISOLATION_LEVEL_READ_COMMITTED),
|
('read committed', ext.ISOLATION_LEVEL_READ_COMMITTED),
|
||||||
|
@ -521,16 +533,27 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
curs.execute('show transaction_isolation;')
|
curs.execute('show transaction_isolation;')
|
||||||
got_name = curs.fetchone()[0]
|
got_name = curs.fetchone()[0]
|
||||||
|
|
||||||
if name is None:
|
|
||||||
curs.execute('show transaction_isolation;')
|
|
||||||
name = curs.fetchone()[0]
|
|
||||||
|
|
||||||
self.assertEqual(name, got_name)
|
self.assertEqual(name, got_name)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
self.assertRaises(ValueError, conn.set_isolation_level, -1)
|
self.assertRaises(ValueError, conn.set_isolation_level, -1)
|
||||||
self.assertRaises(ValueError, conn.set_isolation_level, 5)
|
self.assertRaises(ValueError, conn.set_isolation_level, 5)
|
||||||
|
|
||||||
|
def test_set_isolation_level_autocommit(self):
|
||||||
|
conn = self.connect()
|
||||||
|
curs = conn.cursor()
|
||||||
|
|
||||||
|
conn.set_isolation_level(ext.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||||
|
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_DEFAULT)
|
||||||
|
self.assert_(conn.autocommit)
|
||||||
|
|
||||||
|
conn.isolation_level = 'serializable'
|
||||||
|
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_SERIALIZABLE)
|
||||||
|
self.assert_(conn.autocommit)
|
||||||
|
|
||||||
|
curs.execute('show transaction_isolation;')
|
||||||
|
self.assertEqual(curs.fetchone()[0], 'serializable')
|
||||||
|
|
||||||
def test_set_isolation_level_default(self):
|
def test_set_isolation_level_default(self):
|
||||||
conn = self.connect()
|
conn = self.connect()
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
|
@ -663,8 +686,6 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
def test_isolation_level_closed(self):
|
def test_isolation_level_closed(self):
|
||||||
cnn = self.connect()
|
cnn = self.connect()
|
||||||
cnn.close()
|
cnn.close()
|
||||||
self.assertRaises(psycopg2.InterfaceError, getattr,
|
|
||||||
cnn, 'isolation_level')
|
|
||||||
self.assertRaises(psycopg2.InterfaceError,
|
self.assertRaises(psycopg2.InterfaceError,
|
||||||
cnn.set_isolation_level, 0)
|
cnn.set_isolation_level, 0)
|
||||||
self.assertRaises(psycopg2.InterfaceError,
|
self.assertRaises(psycopg2.InterfaceError,
|
||||||
|
@ -1226,22 +1247,47 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
self.assertRaises(ValueError, self.conn.set_session, 'whatever')
|
self.assertRaises(ValueError, self.conn.set_session, 'whatever')
|
||||||
|
|
||||||
def test_set_read_only(self):
|
def test_set_read_only(self):
|
||||||
cur = self.conn.cursor()
|
self.assert_(self.conn.readonly is None)
|
||||||
self.conn.set_session(readonly=True)
|
|
||||||
cur.execute("SHOW transaction_read_only;")
|
|
||||||
self.assertEqual(cur.fetchone()[0], 'on')
|
|
||||||
self.conn.rollback()
|
|
||||||
cur.execute("SHOW transaction_read_only;")
|
|
||||||
self.assertEqual(cur.fetchone()[0], 'on')
|
|
||||||
self.conn.rollback()
|
|
||||||
|
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
self.conn.set_session(readonly=None)
|
self.conn.set_session(readonly=True)
|
||||||
|
self.assert_(self.conn.readonly is True)
|
||||||
|
cur.execute("SHOW transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
self.conn.rollback()
|
||||||
cur.execute("SHOW transaction_read_only;")
|
cur.execute("SHOW transaction_read_only;")
|
||||||
self.assertEqual(cur.fetchone()[0], 'on')
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
|
|
||||||
self.conn.set_session(readonly=False)
|
self.conn.set_session(readonly=False)
|
||||||
|
self.assert_(self.conn.readonly is False)
|
||||||
|
cur.execute("SHOW transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'off')
|
||||||
|
self.conn.rollback()
|
||||||
|
|
||||||
|
def test_setattr_read_only(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
self.conn.readonly = True
|
||||||
|
self.assert_(self.conn.readonly is True)
|
||||||
|
cur.execute("SHOW transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
self.assertRaises(self.conn.ProgrammingError,
|
||||||
|
setattr, self.conn, 'readonly', False)
|
||||||
|
self.assert_(self.conn.readonly is True)
|
||||||
|
self.conn.rollback()
|
||||||
|
cur.execute("SHOW transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
self.conn.rollback()
|
||||||
|
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
self.conn.readonly = None
|
||||||
|
self.assert_(self.conn.readonly is None)
|
||||||
|
cur.execute("SHOW transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'off') # assume defined by server
|
||||||
|
self.conn.rollback()
|
||||||
|
|
||||||
|
self.conn.readonly = False
|
||||||
|
self.assert_(self.conn.readonly is False)
|
||||||
cur.execute("SHOW transaction_read_only;")
|
cur.execute("SHOW transaction_read_only;")
|
||||||
self.assertEqual(cur.fetchone()[0], 'off')
|
self.assertEqual(cur.fetchone()[0], 'off')
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
|
@ -1264,8 +1310,10 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
|
|
||||||
@skip_before_postgres(9, 1)
|
@skip_before_postgres(9, 1)
|
||||||
def test_set_deferrable(self):
|
def test_set_deferrable(self):
|
||||||
|
self.assert_(self.conn.deferrable is None)
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
self.conn.set_session(readonly=True, deferrable=True)
|
self.conn.set_session(readonly=True, deferrable=True)
|
||||||
|
self.assert_(self.conn.deferrable is True)
|
||||||
cur.execute("SHOW transaction_read_only;")
|
cur.execute("SHOW transaction_read_only;")
|
||||||
self.assertEqual(cur.fetchone()[0], 'on')
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
cur.execute("SHOW transaction_deferrable;")
|
cur.execute("SHOW transaction_deferrable;")
|
||||||
|
@ -1276,6 +1324,7 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
|
|
||||||
self.conn.set_session(deferrable=False)
|
self.conn.set_session(deferrable=False)
|
||||||
|
self.assert_(self.conn.deferrable is False)
|
||||||
cur.execute("SHOW transaction_read_only;")
|
cur.execute("SHOW transaction_read_only;")
|
||||||
self.assertEqual(cur.fetchone()[0], 'on')
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
cur.execute("SHOW transaction_deferrable;")
|
cur.execute("SHOW transaction_deferrable;")
|
||||||
|
@ -1286,6 +1335,54 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
def test_set_deferrable_error(self):
|
def test_set_deferrable_error(self):
|
||||||
self.assertRaises(psycopg2.ProgrammingError,
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
self.conn.set_session, readonly=True, deferrable=True)
|
self.conn.set_session, readonly=True, deferrable=True)
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
setattr, self.conn, 'deferrable', True)
|
||||||
|
|
||||||
|
@skip_before_postgres(9, 1)
|
||||||
|
def test_setattr_deferrable(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
self.conn.deferrable = True
|
||||||
|
self.assert_(self.conn.deferrable is True)
|
||||||
|
cur.execute("SHOW transaction_deferrable;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
self.assertRaises(self.conn.ProgrammingError,
|
||||||
|
setattr, self.conn, 'deferrable', False)
|
||||||
|
self.assert_(self.conn.deferrable is True)
|
||||||
|
self.conn.rollback()
|
||||||
|
cur.execute("SHOW transaction_deferrable;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
self.conn.rollback()
|
||||||
|
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
self.conn.deferrable = None
|
||||||
|
self.assert_(self.conn.deferrable is None)
|
||||||
|
cur.execute("SHOW transaction_deferrable;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'off') # assume defined by server
|
||||||
|
self.conn.rollback()
|
||||||
|
|
||||||
|
self.conn.deferrable = False
|
||||||
|
self.assert_(self.conn.deferrable is False)
|
||||||
|
cur.execute("SHOW transaction_deferrable;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'off')
|
||||||
|
self.conn.rollback()
|
||||||
|
|
||||||
|
def test_mixing_session_attribs(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
self.conn.autocommit = True
|
||||||
|
self.conn.readonly = True
|
||||||
|
|
||||||
|
cur.execute("SHOW transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
|
||||||
|
cur.execute("SHOW default_transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
|
||||||
|
self.conn.autocommit = False
|
||||||
|
cur.execute("SHOW transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
|
||||||
|
cur.execute("SHOW default_transaction_read_only;")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'off')
|
||||||
|
|
||||||
|
|
||||||
class AutocommitTests(ConnectingTestCase):
|
class AutocommitTests(ConnectingTestCase):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user