diff options
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_bsddb.c | 151 |
1 files changed, 110 insertions, 41 deletions
diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c index b74491c..227fe7b 100644 --- a/Modules/_bsddb.c +++ b/Modules/_bsddb.c @@ -93,7 +93,7 @@ /* 40 = 4.0, 33 = 3.3; this will break if the second number is > 9 */ #define DBVER (DB_VERSION_MAJOR * 10 + DB_VERSION_MINOR) -#define PY_BSDDB_VERSION "4.1.5" +#define PY_BSDDB_VERSION "4.1.6" static char *rcs_id = "$Id$"; @@ -141,12 +141,6 @@ static PyInterpreterState* _db_interpreterState = NULL; #endif - -/* What is the default behaviour when DB->get or DBCursor->get returns a - DB_NOTFOUND error? Return None or raise an exception? */ -#define GET_RETURNS_NONE_DEFAULT 1 - - /* Should DB_INCOMPLETE be turned into a warning or an exception? */ #define INCOMPLETE_IS_WARNING 1 @@ -189,12 +183,24 @@ static PyObject* DBPermissionsError; /* EPERM */ /* --------------------------------------------------------------------- */ /* Structure definitions */ +struct behaviourFlags { + /* What is the default behaviour when DB->get or DBCursor->get returns a + DB_NOTFOUND error? Return None or raise an exception? */ + unsigned int getReturnsNone : 1; + /* What is the default behaviour for DBCursor.set* methods when DBCursor->get + * returns a DB_NOTFOUND error? Return None or raise an exception? */ + unsigned int cursorSetReturnsNone : 1; +}; + +#define DEFAULT_GET_RETURNS_NONE 1 +#define DEFAULT_CURSOR_SET_RETURNS_NONE 0 /* 0 in pybsddb < 4.2, python < 2.4 */ + typedef struct { PyObject_HEAD DB_ENV* db_env; u_int32_t flags; /* saved flags from open() */ int closed; - int getReturnsNone; + struct behaviourFlags moduleFlags; } DBEnvObject; @@ -205,7 +211,7 @@ typedef struct { u_int32_t flags; /* saved flags from open() */ u_int32_t setflags; /* saved flags from set_flags() */ int haveStat; - int getReturnsNone; + struct behaviourFlags moduleFlags; #if (DBVER >= 33) PyObject* associateCallback; int primaryDBType; @@ -595,7 +601,7 @@ static PyObject* _DBCursor_get(DBCursorObject* self, int extra_flags, err = self->dbc->c_get(self->dbc, &key, &data, flags); MYDB_END_ALLOW_THREADS; - if ((err == DB_NOTFOUND) && self->mydb->getReturnsNone) { + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) { Py_INCREF(Py_None); retval = Py_None; } @@ -681,9 +687,10 @@ newDBObject(DBEnvObject* arg, int flags) } if (self->myenvobj) - self->getReturnsNone = self->myenvobj->getReturnsNone; + self->moduleFlags = self->myenvobj->moduleFlags; else - self->getReturnsNone = GET_RETURNS_NONE_DEFAULT; + self->moduleFlags.getReturnsNone = DEFAULT_GET_RETURNS_NONE; + self->moduleFlags.cursorSetReturnsNone = DEFAULT_CURSOR_SET_RETURNS_NONE; MYDB_BEGIN_ALLOW_THREADS; err = db_create(&self->db, db_env, flags); @@ -797,7 +804,8 @@ newDBEnvObject(int flags) self->closed = 1; self->flags = flags; - self->getReturnsNone = GET_RETURNS_NONE_DEFAULT; + self->moduleFlags.getReturnsNone = DEFAULT_GET_RETURNS_NONE; + self->moduleFlags.cursorSetReturnsNone = DEFAULT_CURSOR_SET_RETURNS_NONE; MYDB_BEGIN_ALLOW_THREADS; err = db_env_create(&self->db_env, flags); @@ -1182,7 +1190,7 @@ _DB_consume(DBObject* self, PyObject* args, PyObject* kwargs, int consume_flag) err = self->db->get(self->db, txn, &key, &data, flags|consume_flag); MYDB_END_ALLOW_THREADS; - if ((err == DB_NOTFOUND) && self->getReturnsNone) { + if ((err == DB_NOTFOUND) && self->moduleFlags.getReturnsNone) { err = 0; Py_INCREF(Py_None); retval = Py_None; @@ -1324,7 +1332,7 @@ DB_get(DBObject* self, PyObject* args, PyObject* kwargs) Py_INCREF(dfltobj); retval = dfltobj; } - else if ((err == DB_NOTFOUND) && self->getReturnsNone) { + else if ((err == DB_NOTFOUND) && self->moduleFlags.getReturnsNone) { err = 0; Py_INCREF(Py_None); retval = Py_None; @@ -1424,7 +1432,7 @@ DB_get_both(DBObject* self, PyObject* args, PyObject* kwargs) err = self->db->get(self->db, txn, &key, &data, flags); MYDB_END_ALLOW_THREADS; - if ((err == DB_NOTFOUND) && self->getReturnsNone) { + if ((err == DB_NOTFOUND) && self->moduleFlags.getReturnsNone) { err = 0; Py_INCREF(Py_None); retval = Py_None; @@ -1525,6 +1533,11 @@ DB_join(DBObject* self, PyObject* args) free(cursors); RETURN_IF_ERR(); + // FIXME: this is a buggy interface. The returned cursor + // contains internal references to the passed in cursors + // but does not hold python references to them or prevent + // them from being closed prematurely. This can cause + // python to crash when things are done in the wrong order. return (PyObject*) newDBCursorObject(dbc, self); } @@ -2156,14 +2169,18 @@ static PyObject* DB_set_get_returns_none(DBObject* self, PyObject* args) { int flags=0; - int oldValue; + int oldValue=0; if (!PyArg_ParseTuple(args,"i:set_get_returns_none", &flags)) return NULL; CHECK_DB_NOT_CLOSED(self); - oldValue = self->getReturnsNone; - self->getReturnsNone = flags; + if (self->moduleFlags.getReturnsNone) + ++oldValue; + if (self->moduleFlags.cursorSetReturnsNone) + ++oldValue; + self->moduleFlags.getReturnsNone = (flags >= 1); + self->moduleFlags.cursorSetReturnsNone = (flags >= 2); return PyInt_FromLong(oldValue); } @@ -2643,7 +2660,7 @@ DBC_get(DBCursorObject* self, PyObject* args, PyObject *kwargs) MYDB_END_ALLOW_THREADS; - if ((err == DB_NOTFOUND) && self->mydb->getReturnsNone) { + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) { Py_INCREF(Py_None); retval = Py_None; } @@ -2790,7 +2807,11 @@ DBC_set(DBCursorObject* self, PyObject* args, PyObject *kwargs) MYDB_BEGIN_ALLOW_THREADS; err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET); MYDB_END_ALLOW_THREADS; - if (makeDBError(err)) { + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { retval = NULL; } else { @@ -2848,7 +2869,11 @@ DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs) MYDB_BEGIN_ALLOW_THREADS; err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RANGE); MYDB_END_ALLOW_THREADS; - if (makeDBError(err)) { + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { retval = NULL; } else { @@ -2875,19 +2900,15 @@ DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs) return retval; } - static PyObject* -DBC_get_both(DBCursorObject* self, PyObject* args) +_DBC_get_set_both(DBCursorObject* self, PyObject* keyobj, PyObject* dataobj, + int flags, unsigned int returnsNone) { - int err, flags=0; + int err; DBT key, data; - PyObject* retval, *keyobj, *dataobj; - - if (!PyArg_ParseTuple(args, "OO|i:get_both", &keyobj, &dataobj, &flags)) - return NULL; - - CHECK_CURSOR_NOT_CLOSED(self); + PyObject* retval; + // the caller did this: CHECK_CURSOR_NOT_CLOSED(self); if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) return NULL; if (!make_dbt(dataobj, &data)) @@ -2896,7 +2917,11 @@ DBC_get_both(DBCursorObject* self, PyObject* args) MYDB_BEGIN_ALLOW_THREADS; err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_GET_BOTH); MYDB_END_ALLOW_THREADS; - if (makeDBError(err)) { + if ((err == DB_NOTFOUND) && returnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { retval = NULL; } else { @@ -2922,6 +2947,38 @@ DBC_get_both(DBCursorObject* self, PyObject* args) return retval; } +static PyObject* +DBC_get_both(DBCursorObject* self, PyObject* args) +{ + int flags=0; + PyObject *keyobj, *dataobj; + + if (!PyArg_ParseTuple(args, "OO|i:get_both", &keyobj, &dataobj, &flags)) + return NULL; + + // if the cursor is closed, self->mydb may be invalid + CHECK_CURSOR_NOT_CLOSED(self); + + return _DBC_get_set_both(self, keyobj, dataobj, flags, + self->mydb->moduleFlags.getReturnsNone); +} + +static PyObject* +DBC_set_both(DBCursorObject* self, PyObject* args) +{ + int flags=0; + PyObject *keyobj, *dataobj; + + if (!PyArg_ParseTuple(args, "OO|i:set_both", &keyobj, &dataobj, &flags)) + return NULL; + + // if the cursor is closed, self->mydb may be invalid + CHECK_CURSOR_NOT_CLOSED(self); + + return _DBC_get_set_both(self, keyobj, dataobj, flags, + self->mydb->moduleFlags.cursorSetReturnsNone); +} + static PyObject* DBC_set_recno(DBCursorObject* self, PyObject* args, PyObject *kwargs) @@ -2965,7 +3022,11 @@ DBC_set_recno(DBCursorObject* self, PyObject* args, PyObject *kwargs) MYDB_BEGIN_ALLOW_THREADS; err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RECNO); MYDB_END_ALLOW_THREADS; - if (makeDBError(err)) { + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { retval = NULL; } else { /* Can only be used for BTrees, so no need to return int key */ @@ -3010,11 +3071,11 @@ DBC_prev_nodup(DBCursorObject* self, PyObject* args, PyObject *kwargs) static PyObject* DBC_join_item(DBCursorObject* self, PyObject* args) { - int err; + int err, flags=0; DBT key, data; PyObject* retval; - if (!PyArg_ParseTuple(args, ":join_item")) + if (!PyArg_ParseTuple(args, "|i:join_item", &flags)) return NULL; CHECK_CURSOR_NOT_CLOSED(self); @@ -3027,9 +3088,13 @@ DBC_join_item(DBCursorObject* self, PyObject* args) } MYDB_BEGIN_ALLOW_THREADS; - err = self->dbc->c_get(self->dbc, &key, &data, DB_JOIN_ITEM); + err = self->dbc->c_get(self->dbc, &key, &data, flags | DB_JOIN_ITEM); MYDB_END_ALLOW_THREADS; - if (makeDBError(err)) { + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { retval = NULL; } else { @@ -3748,14 +3813,18 @@ static PyObject* DBEnv_set_get_returns_none(DBEnvObject* self, PyObject* args) { int flags=0; - int oldValue; + int oldValue=0; if (!PyArg_ParseTuple(args,"i:set_get_returns_none", &flags)) return NULL; CHECK_ENV_NOT_CLOSED(self); - oldValue = self->getReturnsNone; - self->getReturnsNone = flags; + if (self->moduleFlags.getReturnsNone) + ++oldValue; + if (self->moduleFlags.cursorSetReturnsNone) + ++oldValue; + self->moduleFlags.getReturnsNone = (flags >= 1); + self->moduleFlags.cursorSetReturnsNone = (flags >= 2); return PyInt_FromLong(oldValue); } @@ -3977,7 +4046,7 @@ static PyMethodDef DBCursor_methods[] = { {"set", (PyCFunction)DBC_set, METH_VARARGS|METH_KEYWORDS}, {"set_range", (PyCFunction)DBC_set_range, METH_VARARGS|METH_KEYWORDS}, {"get_both", (PyCFunction)DBC_get_both, METH_VARARGS}, - {"set_both", (PyCFunction)DBC_get_both, METH_VARARGS}, + {"set_both", (PyCFunction)DBC_set_both, METH_VARARGS}, {"set_recno", (PyCFunction)DBC_set_recno, METH_VARARGS|METH_KEYWORDS}, {"consume", (PyCFunction)DBC_consume, METH_VARARGS|METH_KEYWORDS}, {"next_dup", (PyCFunction)DBC_next_dup, METH_VARARGS|METH_KEYWORDS}, |