|
34 | 34 |
|
35 | 35 | #include <string.h> |
36 | 36 |
|
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 */ |
49 | 45 | }; |
50 | 46 |
|
| 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 | +}; |
51 | 60 |
|
52 | 61 | /* Return a new "string" from a char* from the database. |
53 | 62 | * |
@@ -553,50 +562,13 @@ conn_read_encoding(connectionObject *self, PGconn *pgconn) |
553 | 562 | RAISES_NEG int |
554 | 563 | conn_get_isolation_level(connectionObject *self) |
555 | 564 | { |
556 | | - PGresult *pgres = NULL; |
557 | | - char *error = NULL; |
558 | | - int rv = -1; |
559 | | - char *lname; |
560 | | - const IsolationLevel *level; |
561 | | - |
562 | 565 | /* this may get called by async connections too: here's your result */ |
563 | 566 | 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; |
587 | 568 | } |
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; |
597 | 571 | } |
598 | | - |
599 | | - return rv; |
600 | 572 | } |
601 | 573 |
|
602 | 574 |
|
@@ -1208,156 +1180,140 @@ conn_rollback(connectionObject *self) |
1208 | 1180 | return res; |
1209 | 1181 | } |
1210 | 1182 |
|
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) |
1215 | 1185 | { |
1216 | | - PGresult *pgres = NULL; |
1217 | | - char *error = NULL; |
1218 | | - int res = -1; |
1219 | | - |
1220 | 1186 | Py_BEGIN_ALLOW_THREADS; |
1221 | 1187 | pthread_mutex_lock(&self->lock); |
1222 | 1188 |
|
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; |
1231 | 1190 |
|
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 | +} |
1240 | 1196 |
|
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; |
1247 | 1206 | } |
1248 | 1207 | } |
| 1208 | + return level; |
| 1209 | +} |
1249 | 1210 |
|
1250 | | - if (self->autocommit != autocommit) { |
1251 | | - Dprintf("conn_set_session: setting autocommit to %d", autocommit); |
1252 | | - self->autocommit = autocommit; |
1253 | | - } |
1254 | 1211 |
|
1255 | | - res = 0; |
| 1212 | +/* parse a python object into one of the possible isolation level values */ |
1256 | 1213 |
|
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; |
1260 | 1219 |
|
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; |
1263 | 1233 | } |
1264 | 1234 |
|
1265 | | - return res; |
1266 | | -} |
| 1235 | + /* parse from the string -- this includes "default" */ |
1267 | 1236 |
|
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 | + } |
1273 | 1256 |
|
1274 | | - self->autocommit = value; |
| 1257 | + rv = _adjust_isolevel(self, rv); |
1275 | 1258 |
|
1276 | | - pthread_mutex_unlock(&self->lock); |
1277 | | - Py_END_ALLOW_THREADS; |
| 1259 | +exit: |
| 1260 | + Py_XDECREF(pyval); |
1278 | 1261 |
|
1279 | | - return 0; |
| 1262 | + return rv; |
1280 | 1263 | } |
1281 | 1264 |
|
1282 | | -/* conn_switch_isolation_level - switch isolation level on the connection */ |
| 1265 | +/* convert False/True/"default" -> 0/1/2 */ |
1283 | 1266 |
|
1284 | 1267 | RAISES_NEG int |
1285 | | -conn_switch_isolation_level(connectionObject *self, int level) |
| 1268 | +conn_parse_onoff(PyObject *pyval) |
1286 | 1269 | { |
1287 | | - PGresult *pgres = NULL; |
1288 | | - char *error = NULL; |
1289 | | - int curr_level; |
1290 | | - int ret = -1; |
| 1270 | + int rv = -1; |
1291 | 1271 |
|
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 */ |
1299 | 1273 |
|
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 | + } |
1302 | 1287 | } |
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; |
1307 | 1292 | } |
1308 | 1293 |
|
1309 | | - /* Emulate the previous semantic of set_isolation_level() using the |
1310 | | - * functions currently available. */ |
| 1294 | +exit: |
| 1295 | + Py_XDECREF(pyval); |
1311 | 1296 |
|
1312 | | - Py_BEGIN_ALLOW_THREADS; |
1313 | | - pthread_mutex_lock(&self->lock); |
| 1297 | + return rv; |
| 1298 | +} |
1314 | 1299 |
|
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 */ |
1319 | 1301 |
|
| 1302 | +RAISES_NEG int |
| 1303 | +conn_switch_isolation_level(connectionObject *self, int level) |
| 1304 | +{ |
1320 | 1305 | if (level == 0) { |
1321 | | - if ((ret = pq_set_guc_locked(self, |
1322 | | - "default_transaction_isolation", "default", |
1323 | | - &pgres, &error, &_save))) { |
1324 | | - goto endlock; |
1325 | | - } |
1326 | 1306 | self->autocommit = 1; |
1327 | 1307 | } |
1328 | 1308 | 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; |
1347 | 1311 | self->autocommit = 0; |
1348 | 1312 | } |
1349 | 1313 |
|
1350 | 1314 | Dprintf("conn_switch_isolation_level: switched to level %d", level); |
1351 | 1315 |
|
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; |
1361 | 1317 | } |
1362 | 1318 |
|
1363 | 1319 |
|
|
0 commit comments