Skip to content

Commit c1e016e

Browse files
committed
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.
1 parent 9863637 commit c1e016e

File tree

4 files changed

+159
-259
lines changed

4 files changed

+159
-259
lines changed

psycopg/connection.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ extern "C" {
3838
#define ISOLATION_LEVEL_READ_COMMITTED 1
3939
#define ISOLATION_LEVEL_REPEATABLE_READ 2
4040
#define ISOLATION_LEVEL_SERIALIZABLE 3
41+
#define ISOLATION_LEVEL_DEFAULT 5
42+
43+
/* 3-state values on/off/default */
44+
#define STATE_OFF 0
45+
#define STATE_ON 1
46+
#define STATE_DEFAULT 2
4147

4248
/* connection status */
4349
#define CONN_STATUS_SETUP 0
@@ -129,6 +135,11 @@ struct connectionObject {
129135
* codecs.getdecoder('utf8') */
130136
PyObject *pyencoder; /* python codec encoding function */
131137
PyObject *pydecoder; /* python codec decoding function */
138+
139+
/* Values for the transactions characteristics */
140+
int isolevel;
141+
int readonly;
142+
int deferrable;
132143
};
133144

134145
/* map isolation level values into a numeric const */
@@ -155,10 +166,9 @@ HIDDEN void conn_close(connectionObject *self);
155166
HIDDEN void conn_close_locked(connectionObject *self);
156167
RAISES_NEG HIDDEN int conn_commit(connectionObject *self);
157168
RAISES_NEG HIDDEN int conn_rollback(connectionObject *self);
158-
RAISES_NEG HIDDEN int conn_set_session(connectionObject *self, const char *isolevel,
159-
const char *readonly, const char *deferrable,
160-
int autocommit);
161169
HIDDEN int conn_set_autocommit(connectionObject *self, int value);
170+
RAISES_NEG HIDDEN int conn_parse_isolevel(connectionObject *self, PyObject *pyval);
171+
RAISES_NEG HIDDEN int conn_parse_onoff(PyObject *pyval);
162172
RAISES_NEG HIDDEN int conn_switch_isolation_level(connectionObject *self, int level);
163173
RAISES_NEG HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc);
164174
HIDDEN int conn_poll(connectionObject *self);

psycopg/connection_int.c

Lines changed: 118 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,29 @@
3434

3535
#include <string.h>
3636

37-
/* Mapping from isolation level name to value exposed by Python.
38-
*
39-
* Note: ordering matters: to get a valid pre-PG 8 level from one not valid,
40-
* we increase a pointer in this list by one position. */
41-
const IsolationLevel conn_isolevels[] = {
42-
{"", ISOLATION_LEVEL_AUTOCOMMIT},
43-
{"read uncommitted", ISOLATION_LEVEL_READ_UNCOMMITTED},
44-
{"read committed", ISOLATION_LEVEL_READ_COMMITTED},
45-
{"repeatable read", ISOLATION_LEVEL_REPEATABLE_READ},
46-
{"serializable", ISOLATION_LEVEL_SERIALIZABLE},
47-
{"default", -1}, /* never to be found on the server */
48-
{ NULL }
37+
/* String indexes match the ISOLATION_LEVEL_* consts */
38+
const char *srv_isolevels[] = {
39+
NULL, /* autocommit */
40+
"READ COMMITTED",
41+
"REPEATABLE READ",
42+
"SERIALIZABLE",
43+
"READ UNCOMMITTED",
44+
"" /* default */
4945
};
5046

47+
/* Read only false, true */
48+
const char *srv_readonly[] = {
49+
" READ WRITE",
50+
" READ ONLY",
51+
"" /* default */
52+
};
53+
54+
/* Deferrable false, true */
55+
const char *srv_deferrable[] = {
56+
" NOT DEFERRABLE",
57+
" DEFERRABLE",
58+
"" /* default */
59+
};
5160

5261
/* Return a new "string" from a char* from the database.
5362
*
@@ -553,50 +562,13 @@ conn_read_encoding(connectionObject *self, PGconn *pgconn)
553562
RAISES_NEG int
554563
conn_get_isolation_level(connectionObject *self)
555564
{
556-
PGresult *pgres = NULL;
557-
char *error = NULL;
558-
int rv = -1;
559-
char *lname;
560-
const IsolationLevel *level;
561-
562565
/* this may get called by async connections too: here's your result */
563566
if (self->autocommit) {
564-
return 0;
565-
}
566-
567-
Py_BEGIN_ALLOW_THREADS;
568-
pthread_mutex_lock(&self->lock);
569-
570-
if (!(lname = pq_get_guc_locked(self, "default_transaction_isolation",
571-
&pgres, &error, &_save))) {
572-
goto endlock;
573-
}
574-
575-
/* find the value for the requested isolation level */
576-
level = conn_isolevels;
577-
while ((++level)->name) {
578-
if (0 == strcasecmp(level->name, lname)) {
579-
rv = level->value;
580-
break;
581-
}
582-
}
583-
if (-1 == rv) {
584-
error = malloc(256);
585-
PyOS_snprintf(error, 256,
586-
"unexpected isolation level: '%s'", lname);
567+
return ISOLATION_LEVEL_AUTOCOMMIT;
587568
}
588-
589-
free(lname);
590-
591-
endlock:
592-
pthread_mutex_unlock(&self->lock);
593-
Py_END_ALLOW_THREADS;
594-
595-
if (rv < 0) {
596-
pq_complete_error(self, &pgres, &error);
569+
else {
570+
return self->isolevel;
597571
}
598-
599-
return rv;
600572
}
601573

602574

@@ -1208,156 +1180,140 @@ conn_rollback(connectionObject *self)
12081180
return res;
12091181
}
12101182

1211-
RAISES_NEG int
1212-
conn_set_session(connectionObject *self,
1213-
const char *isolevel, const char *readonly, const char *deferrable,
1214-
int autocommit)
1183+
int
1184+
conn_set_autocommit(connectionObject *self, int value)
12151185
{
1216-
PGresult *pgres = NULL;
1217-
char *error = NULL;
1218-
int res = -1;
1219-
12201186
Py_BEGIN_ALLOW_THREADS;
12211187
pthread_mutex_lock(&self->lock);
12221188

1223-
if (isolevel) {
1224-
Dprintf("conn_set_session: setting isolation to %s", isolevel);
1225-
if ((res = pq_set_guc_locked(self,
1226-
"default_transaction_isolation", isolevel,
1227-
&pgres, &error, &_save))) {
1228-
goto endlock;
1229-
}
1230-
}
1189+
self->autocommit = value;
12311190

1232-
if (readonly) {
1233-
Dprintf("conn_set_session: setting read only to %s", readonly);
1234-
if ((res = pq_set_guc_locked(self,
1235-
"default_transaction_read_only", readonly,
1236-
&pgres, &error, &_save))) {
1237-
goto endlock;
1238-
}
1239-
}
1191+
pthread_mutex_unlock(&self->lock);
1192+
Py_END_ALLOW_THREADS;
1193+
1194+
return 0;
1195+
}
12401196

1241-
if (deferrable) {
1242-
Dprintf("conn_set_session: setting deferrable to %s", deferrable);
1243-
if ((res = pq_set_guc_locked(self,
1244-
"default_transaction_deferrable", deferrable,
1245-
&pgres, &error, &_save))) {
1246-
goto endlock;
1197+
/* Promote an isolation level to one of the levels supported by the server */
1198+
1199+
static int _adjust_isolevel(connectionObject *self, int level) {
1200+
if (self->server_version < 80000) {
1201+
if (level == ISOLATION_LEVEL_READ_UNCOMMITTED) {
1202+
level = ISOLATION_LEVEL_READ_COMMITTED;
1203+
}
1204+
else if (level == ISOLATION_LEVEL_REPEATABLE_READ) {
1205+
level = ISOLATION_LEVEL_SERIALIZABLE;
12471206
}
12481207
}
1208+
return level;
1209+
}
12491210

1250-
if (self->autocommit != autocommit) {
1251-
Dprintf("conn_set_session: setting autocommit to %d", autocommit);
1252-
self->autocommit = autocommit;
1253-
}
12541211

1255-
res = 0;
1212+
/* parse a python object into one of the possible isolation level values */
12561213

1257-
endlock:
1258-
pthread_mutex_unlock(&self->lock);
1259-
Py_END_ALLOW_THREADS;
1214+
RAISES_NEG int
1215+
conn_parse_isolevel(connectionObject *self, PyObject *pyval)
1216+
{
1217+
int rv = -1;
1218+
long level;
12601219

1261-
if (res < 0) {
1262-
pq_complete_error(self, &pgres, &error);
1220+
Py_INCREF(pyval); /* for ensure_bytes */
1221+
1222+
/* parse from one of the level constants */
1223+
if (PyInt_Check(pyval)) {
1224+
level = PyInt_AsLong(pyval);
1225+
if (level == -1 && PyErr_Occurred()) { goto exit; }
1226+
if (level < 1 || level > 4) {
1227+
PyErr_SetString(PyExc_ValueError,
1228+
"isolation_level must be between 1 and 4");
1229+
goto exit;
1230+
}
1231+
1232+
rv = level;
12631233
}
12641234

1265-
return res;
1266-
}
1235+
/* parse from the string -- this includes "default" */
12671236

1268-
int
1269-
conn_set_autocommit(connectionObject *self, int value)
1270-
{
1271-
Py_BEGIN_ALLOW_THREADS;
1272-
pthread_mutex_lock(&self->lock);
1237+
else {
1238+
if (!(pyval = psycopg_ensure_bytes(pyval))) {
1239+
goto exit;
1240+
}
1241+
for (level = 1; level <= 4; level++) {
1242+
if (0 == strcasecmp(srv_isolevels[level], Bytes_AS_STRING(pyval))) {
1243+
rv = level;
1244+
break;
1245+
}
1246+
}
1247+
if (rv < 0 && 0 == strcasecmp("default", Bytes_AS_STRING(pyval))) {
1248+
rv = ISOLATION_LEVEL_DEFAULT;
1249+
}
1250+
if (rv < 0) {
1251+
PyErr_Format(PyExc_ValueError,
1252+
"bad value for isolation_level: '%s'", Bytes_AS_STRING(pyval));
1253+
goto exit;
1254+
}
1255+
}
12731256

1274-
self->autocommit = value;
1257+
rv = _adjust_isolevel(self, rv);
12751258

1276-
pthread_mutex_unlock(&self->lock);
1277-
Py_END_ALLOW_THREADS;
1259+
exit:
1260+
Py_XDECREF(pyval);
12781261

1279-
return 0;
1262+
return rv;
12801263
}
12811264

1282-
/* conn_switch_isolation_level - switch isolation level on the connection */
1265+
/* convert False/True/"default" -> 0/1/2 */
12831266

12841267
RAISES_NEG int
1285-
conn_switch_isolation_level(connectionObject *self, int level)
1268+
conn_parse_onoff(PyObject *pyval)
12861269
{
1287-
PGresult *pgres = NULL;
1288-
char *error = NULL;
1289-
int curr_level;
1290-
int ret = -1;
1270+
int rv = -1;
12911271

1292-
/* use only supported levels on older PG versions */
1293-
if (self->server_version < 80000) {
1294-
if (level == ISOLATION_LEVEL_READ_UNCOMMITTED)
1295-
level = ISOLATION_LEVEL_READ_COMMITTED;
1296-
else if (level == ISOLATION_LEVEL_REPEATABLE_READ)
1297-
level = ISOLATION_LEVEL_SERIALIZABLE;
1298-
}
1272+
Py_INCREF(pyval); /* for ensure_bytes */
12991273

1300-
if (-1 == (curr_level = conn_get_isolation_level(self))) {
1301-
return -1;
1274+
if (PyUnicode_CheckExact(pyval) || Bytes_CheckExact(pyval)) {
1275+
if (!(pyval = psycopg_ensure_bytes(pyval))) {
1276+
goto exit;
1277+
}
1278+
if (0 == strcasecmp("default", Bytes_AS_STRING(pyval))) {
1279+
rv = STATE_DEFAULT;
1280+
}
1281+
else {
1282+
PyErr_Format(PyExc_ValueError,
1283+
"the only string accepted is 'default'; got %s",
1284+
Bytes_AS_STRING(pyval));
1285+
goto exit;
1286+
}
13021287
}
1303-
1304-
if (curr_level == level) {
1305-
/* no need to change level */
1306-
return 0;
1288+
else {
1289+
int istrue;
1290+
if (0 > (istrue = PyObject_IsTrue(pyval))) { goto exit; }
1291+
rv = istrue ? STATE_ON : STATE_OFF;
13071292
}
13081293

1309-
/* Emulate the previous semantic of set_isolation_level() using the
1310-
* functions currently available. */
1294+
exit:
1295+
Py_XDECREF(pyval);
13111296

1312-
Py_BEGIN_ALLOW_THREADS;
1313-
pthread_mutex_lock(&self->lock);
1297+
return rv;
1298+
}
13141299

1315-
/* terminate the current transaction if any */
1316-
if ((ret = pq_abort_locked(self, &pgres, &error, &_save))) {
1317-
goto endlock;
1318-
}
1300+
/* conn_switch_isolation_level - switch isolation level on the connection */
13191301

1302+
RAISES_NEG int
1303+
conn_switch_isolation_level(connectionObject *self, int level)
1304+
{
13201305
if (level == 0) {
1321-
if ((ret = pq_set_guc_locked(self,
1322-
"default_transaction_isolation", "default",
1323-
&pgres, &error, &_save))) {
1324-
goto endlock;
1325-
}
13261306
self->autocommit = 1;
13271307
}
13281308
else {
1329-
/* find the name of the requested level */
1330-
const IsolationLevel *isolevel = conn_isolevels;
1331-
while ((++isolevel)->name) {
1332-
if (level == isolevel->value) {
1333-
break;
1334-
}
1335-
}
1336-
if (!isolevel->name) {
1337-
ret = -1;
1338-
error = strdup("bad isolation level value");
1339-
goto endlock;
1340-
}
1341-
1342-
if ((ret = pq_set_guc_locked(self,
1343-
"default_transaction_isolation", isolevel->name,
1344-
&pgres, &error, &_save))) {
1345-
goto endlock;
1346-
}
1309+
level = _adjust_isolevel(self, level);
1310+
self->isolevel = level;
13471311
self->autocommit = 0;
13481312
}
13491313

13501314
Dprintf("conn_switch_isolation_level: switched to level %d", level);
13511315

1352-
endlock:
1353-
pthread_mutex_unlock(&self->lock);
1354-
Py_END_ALLOW_THREADS;
1355-
1356-
if (ret < 0) {
1357-
pq_complete_error(self, &pgres, &error);
1358-
}
1359-
1360-
return ret;
1316+
return 0;
13611317
}
13621318

13631319

0 commit comments

Comments
 (0)