Kaydet (Commit) 05a24d7f authored tarafından Gregory P. Smith's avatar Gregory P. Smith

* closes SF bug/patch 967763

  - fixes various memory leaks found by valgrind and a follup closer
    code inspection of the bsddb module.  (merges r1.32 of _bsddb.c
    and an associated test case)
  - also merges the one line r1.37 _bsddb.c fix that fixes a leak on
    the rare DBEnv creation failed error path.
üst 923fae44
...@@ -133,6 +133,15 @@ class SimpleRecnoTestCase(unittest.TestCase): ...@@ -133,6 +133,15 @@ class SimpleRecnoTestCase(unittest.TestCase):
if verbose: if verbose:
print rec print rec
# test that non-existant key lookups work (and that
# DBC_set_range doesn't have a memleak under valgrind)
old_grn = d.set_get_returns_none(2)
rec = c.set_range(999999)
assert rec == None
if verbose:
print rec
d.set_get_returns_none(old_grn)
c.close() c.close()
d.close() d.close()
...@@ -177,6 +186,8 @@ class SimpleRecnoTestCase(unittest.TestCase): ...@@ -177,6 +186,8 @@ class SimpleRecnoTestCase(unittest.TestCase):
""" """
source = os.path.join(os.path.dirname(sys.argv[0]), source = os.path.join(os.path.dirname(sys.argv[0]),
'db_home/test_recno.txt') 'db_home/test_recno.txt')
if not os.path.isdir('db_home'):
os.mkdir('db_home')
f = open(source, 'w') # create the file f = open(source, 'w') # create the file
f.close() f.close()
......
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
/* 40 = 4.0, 33 = 3.3; this will break if the second number is > 9 */ /* 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 DBVER (DB_VERSION_MAJOR * 10 + DB_VERSION_MINOR)
#define PY_BSDDB_VERSION "4.2.0.2" #define PY_BSDDB_VERSION "4.2.0.5"
static char *rcs_id = "$Id$"; static char *rcs_id = "$Id$";
...@@ -287,7 +287,7 @@ staticforward PyTypeObject DB_Type, DBCursor_Type, DBEnv_Type, DBTxn_Type, DBLoc ...@@ -287,7 +287,7 @@ staticforward PyTypeObject DB_Type, DBCursor_Type, DBEnv_Type, DBTxn_Type, DBLoc
#define CLEAR_DBT(dbt) (memset(&(dbt), 0, sizeof(dbt))) #define CLEAR_DBT(dbt) (memset(&(dbt), 0, sizeof(dbt)))
#define FREE_DBT(dbt) if ((dbt.flags & (DB_DBT_MALLOC|DB_DBT_REALLOC)) && \ #define FREE_DBT(dbt) if ((dbt.flags & (DB_DBT_MALLOC|DB_DBT_REALLOC)) && \
dbt.data != NULL) { free(dbt.data); } dbt.data != NULL) { free(dbt.data); dbt.data = NULL; }
static int makeDBError(int err); static int makeDBError(int err);
...@@ -320,7 +320,7 @@ static int make_dbt(PyObject* obj, DBT* dbt) ...@@ -320,7 +320,7 @@ static int make_dbt(PyObject* obj, DBT* dbt)
} }
else if (!PyArg_Parse(obj, "s#", &dbt->data, &dbt->size)) { else if (!PyArg_Parse(obj, "s#", &dbt->data, &dbt->size)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"Key and Data values must be of type string or None."); "Data values must be of type string or None.");
return 0; return 0;
} }
return 1; return 1;
...@@ -330,7 +330,7 @@ static int make_dbt(PyObject* obj, DBT* dbt) ...@@ -330,7 +330,7 @@ static int make_dbt(PyObject* obj, DBT* dbt)
/* Recno and Queue DBs can have integer keys. This function figures out /* Recno and Queue DBs can have integer keys. This function figures out
what's been given, verifies that it's allowed, and then makes the DBT. what's been given, verifies that it's allowed, and then makes the DBT.
Caller should call FREE_DBT(key) when done. */ Caller MUST call FREE_DBT(key) when done. */
static int static int
make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags) make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags)
{ {
...@@ -830,7 +830,7 @@ newDBEnvObject(int flags) ...@@ -830,7 +830,7 @@ newDBEnvObject(int flags)
static void static void
DBEnv_dealloc(DBEnvObject* self) DBEnv_dealloc(DBEnvObject* self)
{ {
if (!self->closed) { if (self->db_env) {
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
self->db_env->close(self->db_env, 0); self->db_env->close(self->db_env, 0);
MYDB_END_ALLOW_THREADS; MYDB_END_ALLOW_THREADS;
...@@ -1269,11 +1269,15 @@ DB_delete(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1269,11 +1269,15 @@ DB_delete(DBObject* self, PyObject* args, PyObject* kwargs)
CHECK_DB_NOT_CLOSED(self); CHECK_DB_NOT_CLOSED(self);
if (!make_key_dbt(self, keyobj, &key, NULL)) if (!make_key_dbt(self, keyobj, &key, NULL))
return NULL; return NULL;
if (!checkTxnObj(txnobj, &txn)) if (!checkTxnObj(txnobj, &txn)) {
FREE_DBT(key);
return NULL; return NULL;
}
if (-1 == _DB_delete(self, txn, &key, 0)) if (-1 == _DB_delete(self, txn, &key, 0)) {
FREE_DBT(key);
return NULL; return NULL;
}
FREE_DBT(key); FREE_DBT(key);
RETURN_NONE(); RETURN_NONE();
...@@ -1319,16 +1323,20 @@ DB_get(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1319,16 +1323,20 @@ DB_get(DBObject* self, PyObject* args, PyObject* kwargs)
CHECK_DB_NOT_CLOSED(self); CHECK_DB_NOT_CLOSED(self);
if (!make_key_dbt(self, keyobj, &key, &flags)) if (!make_key_dbt(self, keyobj, &key, &flags))
return NULL; return NULL;
if (!checkTxnObj(txnobj, &txn)) if (!checkTxnObj(txnobj, &txn)) {
FREE_DBT(key);
return NULL; return NULL;
}
CLEAR_DBT(data); CLEAR_DBT(data);
if (CHECK_DBFLAG(self, DB_THREAD)) { if (CHECK_DBFLAG(self, DB_THREAD)) {
/* Tell BerkeleyDB to malloc the return value (thread safe) */ /* Tell BerkeleyDB to malloc the return value (thread safe) */
data.flags = DB_DBT_MALLOC; data.flags = DB_DBT_MALLOC;
} }
if (!add_partial_dbt(&data, dlen, doff)) if (!add_partial_dbt(&data, dlen, doff)) {
FREE_DBT(key);
return NULL; return NULL;
}
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
err = self->db->get(self->db, txn, &key, &data, flags); err = self->db->get(self->db, txn, &key, &data, flags);
...@@ -1350,9 +1358,9 @@ DB_get(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1350,9 +1358,9 @@ DB_get(DBObject* self, PyObject* args, PyObject* kwargs)
data.size); data.size);
else /* return just the data */ else /* return just the data */
retval = PyString_FromStringAndSize((char*)data.data, data.size); retval = PyString_FromStringAndSize((char*)data.data, data.size);
FREE_DBT(key);
FREE_DBT(data); FREE_DBT(data);
} }
FREE_DBT(key);
RETURN_IF_ERR(); RETURN_IF_ERR();
return retval; return retval;
...@@ -1377,8 +1385,10 @@ DB_get_size(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1377,8 +1385,10 @@ DB_get_size(DBObject* self, PyObject* args, PyObject* kwargs)
CHECK_DB_NOT_CLOSED(self); CHECK_DB_NOT_CLOSED(self);
if (!make_key_dbt(self, keyobj, &key, &flags)) if (!make_key_dbt(self, keyobj, &key, &flags))
return NULL; return NULL;
if (!checkTxnObj(txnobj, &txn)) if (!checkTxnObj(txnobj, &txn)) {
FREE_DBT(key);
return NULL; return NULL;
}
CLEAR_DBT(data); CLEAR_DBT(data);
/* We don't allocate any memory, forcing a ENOMEM error and thus /* We don't allocate any memory, forcing a ENOMEM error and thus
...@@ -1420,10 +1430,12 @@ DB_get_both(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1420,10 +1430,12 @@ DB_get_both(DBObject* self, PyObject* args, PyObject* kwargs)
CHECK_DB_NOT_CLOSED(self); CHECK_DB_NOT_CLOSED(self);
if (!make_key_dbt(self, keyobj, &key, NULL)) if (!make_key_dbt(self, keyobj, &key, NULL))
return NULL; return NULL;
if (!make_dbt(dataobj, &data)) if ( !make_dbt(dataobj, &data) ||
return NULL; !checkTxnObj(txnobj, &txn) )
if (!checkTxnObj(txnobj, &txn)) {
FREE_DBT(key);
return NULL; return NULL;
}
flags |= DB_GET_BOTH; flags |= DB_GET_BOTH;
...@@ -1690,10 +1702,15 @@ DB_put(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1690,10 +1702,15 @@ DB_put(DBObject* self, PyObject* args, PyObject* kwargs)
return NULL; return NULL;
CHECK_DB_NOT_CLOSED(self); CHECK_DB_NOT_CLOSED(self);
if (!make_key_dbt(self, keyobj, &key, NULL)) return NULL; if (!make_key_dbt(self, keyobj, &key, NULL))
if (!make_dbt(dataobj, &data)) return NULL; return NULL;
if (!add_partial_dbt(&data, dlen, doff)) return NULL; if ( !make_dbt(dataobj, &data) ||
if (!checkTxnObj(txnobj, &txn)) return NULL; !add_partial_dbt(&data, dlen, doff) ||
!checkTxnObj(txnobj, &txn) )
{
FREE_DBT(key);
return NULL;
}
if (-1 == _DB_put(self, txn, &key, &data, flags)) { if (-1 == _DB_put(self, txn, &key, &data, flags)) {
FREE_DBT(key); FREE_DBT(key);
...@@ -2361,8 +2378,10 @@ DB_has_key(DBObject* self, PyObject* args) ...@@ -2361,8 +2378,10 @@ DB_has_key(DBObject* self, PyObject* args)
CHECK_DB_NOT_CLOSED(self); CHECK_DB_NOT_CLOSED(self);
if (!make_key_dbt(self, keyobj, &key, NULL)) if (!make_key_dbt(self, keyobj, &key, NULL))
return NULL; return NULL;
if (!checkTxnObj(txnobj, &txn)) if (!checkTxnObj(txnobj, &txn)) {
FREE_DBT(key);
return NULL; return NULL;
}
/* This causes ENOMEM to be returned when the db has the key because /* This causes ENOMEM to be returned when the db has the key because
it has a record but can't allocate a buffer for the data. This saves it has a record but can't allocate a buffer for the data. This saves
...@@ -2663,21 +2682,24 @@ DBC_get(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -2663,21 +2682,24 @@ DBC_get(DBCursorObject* self, PyObject* args, PyObject *kwargs)
if (keyobj && !make_key_dbt(self->mydb, keyobj, &key, NULL)) if (keyobj && !make_key_dbt(self->mydb, keyobj, &key, NULL))
return NULL; return NULL;
if (dataobj && !make_dbt(dataobj, &data)) if ( (dataobj && !make_dbt(dataobj, &data)) ||
return NULL; (!add_partial_dbt(&data, dlen, doff)) )
if (!add_partial_dbt(&data, dlen, doff)) {
FREE_DBT(key);
return NULL; return NULL;
}
if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { if (CHECK_DBFLAG(self->mydb, DB_THREAD)) {
data.flags = DB_DBT_MALLOC; data.flags = DB_DBT_MALLOC;
key.flags = DB_DBT_MALLOC; if (!(key.flags & DB_DBT_REALLOC)) {
key.flags |= DB_DBT_MALLOC;
}
} }
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
err = self->dbc->c_get(self->dbc, &key, &data, flags); err = self->dbc->c_get(self->dbc, &key, &data, flags);
MYDB_END_ALLOW_THREADS; MYDB_END_ALLOW_THREADS;
if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) { if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) {
Py_INCREF(Py_None); Py_INCREF(Py_None);
retval = Py_None; retval = Py_None;
...@@ -2702,9 +2724,9 @@ DBC_get(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -2702,9 +2724,9 @@ DBC_get(DBCursorObject* self, PyObject* args, PyObject *kwargs)
data.data, data.size); data.data, data.size);
break; break;
} }
FREE_DBT(key);
FREE_DBT(data); FREE_DBT(data);
} }
FREE_DBT(key);
return retval; return retval;
} }
...@@ -2781,9 +2803,12 @@ DBC_put(DBCursorObject* self, PyObject* args, PyObject* kwargs) ...@@ -2781,9 +2803,12 @@ DBC_put(DBCursorObject* self, PyObject* args, PyObject* kwargs)
if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) if (!make_key_dbt(self->mydb, keyobj, &key, NULL))
return NULL; return NULL;
if (!make_dbt(dataobj, &data)) if (!make_dbt(dataobj, &data) ||
!add_partial_dbt(&data, dlen, doff) )
{
FREE_DBT(key);
return NULL; return NULL;
if (!add_partial_dbt(&data, dlen, doff)) return NULL; }
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
err = self->dbc->c_put(self->dbc, &key, &data, flags); err = self->dbc->c_put(self->dbc, &key, &data, flags);
...@@ -2819,8 +2844,10 @@ DBC_set(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -2819,8 +2844,10 @@ DBC_set(DBCursorObject* self, PyObject* args, PyObject *kwargs)
/* Tell BerkeleyDB to malloc the return value (thread safe) */ /* Tell BerkeleyDB to malloc the return value (thread safe) */
data.flags = DB_DBT_MALLOC; data.flags = DB_DBT_MALLOC;
} }
if (!add_partial_dbt(&data, dlen, doff)) if (!add_partial_dbt(&data, dlen, doff)) {
FREE_DBT(key);
return NULL; return NULL;
}
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET); err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET);
...@@ -2849,9 +2876,9 @@ DBC_set(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -2849,9 +2876,9 @@ DBC_set(DBCursorObject* self, PyObject* args, PyObject *kwargs)
data.data, data.size); data.data, data.size);
break; break;
} }
FREE_DBT(key);
FREE_DBT(data); FREE_DBT(data);
} }
FREE_DBT(key);
return retval; return retval;
} }
...@@ -2877,13 +2904,18 @@ DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs) ...@@ -2877,13 +2904,18 @@ DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs)
return NULL; return NULL;
CLEAR_DBT(data); CLEAR_DBT(data);
if (!add_partial_dbt(&data, dlen, doff)) {
FREE_DBT(key);
return NULL;
}
if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { if (CHECK_DBFLAG(self->mydb, DB_THREAD)) {
/* Tell BerkeleyDB to malloc the return value (thread safe) */ /* Tell BerkeleyDB to malloc the return value (thread safe) */
data.flags = DB_DBT_MALLOC; data.flags |= DB_DBT_MALLOC;
key.flags = DB_DBT_MALLOC; /* only BTREE databases will return anything in the key */
if (!(key.flags & DB_DBT_REALLOC) && _DB_get_type(self->mydb) == DB_BTREE) {
key.flags |= DB_DBT_MALLOC;
}
} }
if (!add_partial_dbt(&data, dlen, doff))
return NULL;
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RANGE); err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RANGE);
MYDB_END_ALLOW_THREADS; MYDB_END_ALLOW_THREADS;
...@@ -2911,17 +2943,14 @@ DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs) ...@@ -2911,17 +2943,14 @@ DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs)
data.data, data.size); data.data, data.size);
break; break;
} }
if (_DB_get_type(self->mydb) == DB_BTREE) {
/* the only time a malloced key is returned is when we
* call this on a BTree database because it performs
* partial matching and needs to return the real key.
* All others leave key untouched [where calling free()
* on it would often segfault].
*/
FREE_DBT(key); FREE_DBT(key);
}
FREE_DBT(data); FREE_DBT(data);
} }
/* the only time REALLOC should be set is if we used an integer
* key that make_dbt_key malloc'd for us. always free these. */
if (key.flags & DB_DBT_REALLOC) {
FREE_DBT(key);
}
return retval; return retval;
} }
...@@ -2937,8 +2966,10 @@ _DBC_get_set_both(DBCursorObject* self, PyObject* keyobj, PyObject* dataobj, ...@@ -2937,8 +2966,10 @@ _DBC_get_set_both(DBCursorObject* self, PyObject* keyobj, PyObject* dataobj,
/* the caller did this: CHECK_CURSOR_NOT_CLOSED(self); */ /* the caller did this: CHECK_CURSOR_NOT_CLOSED(self); */
if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) if (!make_key_dbt(self->mydb, keyobj, &key, NULL))
return NULL; return NULL;
if (!make_dbt(dataobj, &data)) if (!make_dbt(dataobj, &data)) {
FREE_DBT(key);
return NULL; return NULL;
}
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_GET_BOTH); err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_GET_BOTH);
...@@ -3042,8 +3073,10 @@ DBC_set_recno(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -3042,8 +3073,10 @@ DBC_set_recno(DBCursorObject* self, PyObject* args, PyObject *kwargs)
/* Tell BerkeleyDB to malloc the return value (thread safe) */ /* Tell BerkeleyDB to malloc the return value (thread safe) */
data.flags = DB_DBT_MALLOC; data.flags = DB_DBT_MALLOC;
} }
if (!add_partial_dbt(&data, dlen, doff)) if (!add_partial_dbt(&data, dlen, doff)) {
FREE_DBT(key);
return NULL; return NULL;
}
MYDB_BEGIN_ALLOW_THREADS; MYDB_BEGIN_ALLOW_THREADS;
err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RECNO); err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RECNO);
...@@ -3058,9 +3091,9 @@ DBC_set_recno(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -3058,9 +3091,9 @@ DBC_set_recno(DBCursorObject* self, PyObject* args, PyObject *kwargs)
else { /* Can only be used for BTrees, so no need to return int key */ else { /* Can only be used for BTrees, so no need to return int key */
retval = Py_BuildValue("s#s#", key.data, key.size, retval = Py_BuildValue("s#s#", key.data, key.size,
data.data, data.size); data.data, data.size);
FREE_DBT(key);
FREE_DBT(data); FREE_DBT(data);
} }
FREE_DBT(key);
return retval; return retval;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment