/Modules/_bsddb.c
http://unladen-swallow.googlecode.com/ · C · 7581 lines · 6090 code · 1117 blank · 374 comment · 779 complexity · b1b0974700e2265929347f1ae37b6bf2 MD5 · raw file
Large files are truncated click here to view the full file
- /*----------------------------------------------------------------------
- Copyright (c) 1999-2001, Digital Creations, Fredericksburg, VA, USA
- and Andrew Kuchling. All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
- o Redistributions of source code must retain the above copyright
- notice, this list of conditions, and the disclaimer that follows.
- o Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions, and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- o Neither the name of Digital Creations nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS
- IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
- CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- DAMAGE.
- ------------------------------------------------------------------------*/
- /*
- * Handwritten code to wrap version 3.x of the Berkeley DB library,
- * written to replace a SWIG-generated file. It has since been updated
- * to compile with Berkeley DB versions 3.2 through 4.2.
- *
- * This module was started by Andrew Kuchling to remove the dependency
- * on SWIG in a package by Gregory P. Smith who based his work on a
- * similar package by Robin Dunn <robin@alldunn.com> which wrapped
- * Berkeley DB 2.7.x.
- *
- * Development of this module then returned full circle back to Robin Dunn
- * who worked on behalf of Digital Creations to complete the wrapping of
- * the DB 3.x API and to build a solid unit test suite. Robin has
- * since gone onto other projects (wxPython).
- *
- * Gregory P. Smith <greg@krypto.org> was once again the maintainer.
- *
- * Since January 2008, new maintainer is Jesus Cea <jcea@jcea.es>.
- * Jesus Cea licenses this code to PSF under a Contributor Agreement.
- *
- * Use the pybsddb-users@lists.sf.net mailing list for all questions.
- * Things can change faster than the header of this file is updated. This
- * file is shared with the PyBSDDB project at SourceForge:
- *
- * http://pybsddb.sf.net
- *
- * This file should remain backward compatible with Python 2.1, but see PEP
- * 291 for the most current backward compatibility requirements:
- *
- * http://www.python.org/peps/pep-0291.html
- *
- * This module contains 6 types:
- *
- * DB (Database)
- * DBCursor (Database Cursor)
- * DBEnv (database environment)
- * DBTxn (An explicit database transaction)
- * DBLock (A lock handle)
- * DBSequence (Sequence)
- *
- */
- /* --------------------------------------------------------------------- */
- /*
- * Portions of this module, associated unit tests and build scripts are the
- * result of a contract with The Written Word (http://thewrittenword.com/)
- * Many thanks go out to them for causing me to raise the bar on quality and
- * functionality, resulting in a better bsddb3 package for all of us to use.
- *
- * --Robin
- */
- /* --------------------------------------------------------------------- */
- #include <stddef.h> /* for offsetof() */
- #include <Python.h>
- #define COMPILING_BSDDB_C
- #include "bsddb.h"
- #undef COMPILING_BSDDB_C
- static char *rcs_id = "$Id: _bsddb.c 66568 2008-09-23 18:54:08Z jesus.cea $";
- /* --------------------------------------------------------------------- */
- /* Various macro definitions */
- #if (PY_VERSION_HEX < 0x02050000)
- typedef int Py_ssize_t;
- #endif
- #if (PY_VERSION_HEX < 0x02060000) /* really: before python trunk r63675 */
- /* This code now uses PyBytes* API function names instead of PyString*.
- * These #defines map to their equivalent on earlier python versions. */
- #define PyBytes_FromStringAndSize PyString_FromStringAndSize
- #define PyBytes_FromString PyString_FromString
- #define PyBytes_AsStringAndSize PyString_AsStringAndSize
- #define PyBytes_Check PyString_Check
- #define PyBytes_GET_SIZE PyString_GET_SIZE
- #define PyBytes_AS_STRING PyString_AS_STRING
- #endif
- #if (PY_VERSION_HEX >= 0x03000000)
- #define NUMBER_Check PyLong_Check
- #define NUMBER_AsLong PyLong_AsLong
- #define NUMBER_FromLong PyLong_FromLong
- #else
- #define NUMBER_Check PyInt_Check
- #define NUMBER_AsLong PyInt_AsLong
- #define NUMBER_FromLong PyInt_FromLong
- #endif
- #ifdef WITH_THREAD
- /* These are for when calling Python --> C */
- #define MYDB_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS;
- #define MYDB_END_ALLOW_THREADS Py_END_ALLOW_THREADS;
- /* For 2.3, use the PyGILState_ calls */
- #if (PY_VERSION_HEX >= 0x02030000)
- #define MYDB_USE_GILSTATE
- #endif
- /* and these are for calling C --> Python */
- #if defined(MYDB_USE_GILSTATE)
- #define MYDB_BEGIN_BLOCK_THREADS \
- PyGILState_STATE __savestate = PyGILState_Ensure();
- #define MYDB_END_BLOCK_THREADS \
- PyGILState_Release(__savestate);
- #else /* MYDB_USE_GILSTATE */
- /* Pre GILState API - do it the long old way */
- static PyInterpreterState* _db_interpreterState = NULL;
- #define MYDB_BEGIN_BLOCK_THREADS { \
- PyThreadState* prevState; \
- PyThreadState* newState; \
- PyEval_AcquireLock(); \
- newState = PyThreadState_New(_db_interpreterState); \
- prevState = PyThreadState_Swap(newState);
- #define MYDB_END_BLOCK_THREADS \
- newState = PyThreadState_Swap(prevState); \
- PyThreadState_Clear(newState); \
- PyEval_ReleaseLock(); \
- PyThreadState_Delete(newState); \
- }
- #endif /* MYDB_USE_GILSTATE */
- #else
- /* Compiled without threads - avoid all this cruft */
- #define MYDB_BEGIN_ALLOW_THREADS
- #define MYDB_END_ALLOW_THREADS
- #define MYDB_BEGIN_BLOCK_THREADS
- #define MYDB_END_BLOCK_THREADS
- #endif
- /* Should DB_INCOMPLETE be turned into a warning or an exception? */
- #define INCOMPLETE_IS_WARNING 1
- /* --------------------------------------------------------------------- */
- /* Exceptions */
- static PyObject* DBError; /* Base class, all others derive from this */
- static PyObject* DBCursorClosedError; /* raised when trying to use a closed cursor object */
- static PyObject* DBKeyEmptyError; /* DB_KEYEMPTY: also derives from KeyError */
- static PyObject* DBKeyExistError; /* DB_KEYEXIST */
- static PyObject* DBLockDeadlockError; /* DB_LOCK_DEADLOCK */
- static PyObject* DBLockNotGrantedError; /* DB_LOCK_NOTGRANTED */
- static PyObject* DBNotFoundError; /* DB_NOTFOUND: also derives from KeyError */
- static PyObject* DBOldVersionError; /* DB_OLD_VERSION */
- static PyObject* DBRunRecoveryError; /* DB_RUNRECOVERY */
- static PyObject* DBVerifyBadError; /* DB_VERIFY_BAD */
- static PyObject* DBNoServerError; /* DB_NOSERVER */
- static PyObject* DBNoServerHomeError; /* DB_NOSERVER_HOME */
- static PyObject* DBNoServerIDError; /* DB_NOSERVER_ID */
- static PyObject* DBPageNotFoundError; /* DB_PAGE_NOTFOUND */
- static PyObject* DBSecondaryBadError; /* DB_SECONDARY_BAD */
- #if !INCOMPLETE_IS_WARNING
- static PyObject* DBIncompleteError; /* DB_INCOMPLETE */
- #endif
- static PyObject* DBInvalidArgError; /* EINVAL */
- static PyObject* DBAccessError; /* EACCES */
- static PyObject* DBNoSpaceError; /* ENOSPC */
- static PyObject* DBNoMemoryError; /* DB_BUFFER_SMALL (ENOMEM when < 4.3) */
- static PyObject* DBAgainError; /* EAGAIN */
- static PyObject* DBBusyError; /* EBUSY */
- static PyObject* DBFileExistsError; /* EEXIST */
- static PyObject* DBNoSuchFileError; /* ENOENT */
- static PyObject* DBPermissionsError; /* EPERM */
- #if (DBVER >= 42)
- static PyObject* DBRepHandleDeadError; /* DB_REP_HANDLE_DEAD */
- #endif
- static PyObject* DBRepUnavailError; /* DB_REP_UNAVAIL */
- #if (DBVER < 43)
- #define DB_BUFFER_SMALL ENOMEM
- #endif
- /* --------------------------------------------------------------------- */
- /* Structure definitions */
- #if PYTHON_API_VERSION < 1010
- #error "Python 2.1 or later required"
- #endif
- /* Defaults for moduleFlags in DBEnvObject and DBObject. */
- #define DEFAULT_GET_RETURNS_NONE 1
- #define DEFAULT_CURSOR_SET_RETURNS_NONE 1 /* 0 in pybsddb < 4.2, python < 2.4 */
- /* See comment in Python 2.6 "object.h" */
- #ifndef staticforward
- #define staticforward static
- #endif
- #ifndef statichere
- #define statichere static
- #endif
- staticforward PyTypeObject DB_Type, DBCursor_Type, DBEnv_Type, DBTxn_Type,
- DBLock_Type;
- #if (DBVER >= 43)
- staticforward PyTypeObject DBSequence_Type;
- #endif
- #ifndef Py_TYPE
- /* for compatibility with Python 2.5 and earlier */
- #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
- #endif
- #define DBObject_Check(v) (Py_TYPE(v) == &DB_Type)
- #define DBCursorObject_Check(v) (Py_TYPE(v) == &DBCursor_Type)
- #define DBEnvObject_Check(v) (Py_TYPE(v) == &DBEnv_Type)
- #define DBTxnObject_Check(v) (Py_TYPE(v) == &DBTxn_Type)
- #define DBLockObject_Check(v) (Py_TYPE(v) == &DBLock_Type)
- #if (DBVER >= 43)
- #define DBSequenceObject_Check(v) (Py_TYPE(v) == &DBSequence_Type)
- #endif
- #if (DBVER < 46)
- #define _DBC_close(dbc) dbc->c_close(dbc)
- #define _DBC_count(dbc,a,b) dbc->c_count(dbc,a,b)
- #define _DBC_del(dbc,a) dbc->c_del(dbc,a)
- #define _DBC_dup(dbc,a,b) dbc->c_dup(dbc,a,b)
- #define _DBC_get(dbc,a,b,c) dbc->c_get(dbc,a,b,c)
- #define _DBC_pget(dbc,a,b,c,d) dbc->c_pget(dbc,a,b,c,d)
- #define _DBC_put(dbc,a,b,c) dbc->c_put(dbc,a,b,c)
- #else
- #define _DBC_close(dbc) dbc->close(dbc)
- #define _DBC_count(dbc,a,b) dbc->count(dbc,a,b)
- #define _DBC_del(dbc,a) dbc->del(dbc,a)
- #define _DBC_dup(dbc,a,b) dbc->dup(dbc,a,b)
- #define _DBC_get(dbc,a,b,c) dbc->get(dbc,a,b,c)
- #define _DBC_pget(dbc,a,b,c,d) dbc->pget(dbc,a,b,c,d)
- #define _DBC_put(dbc,a,b,c) dbc->put(dbc,a,b,c)
- #endif
- /* --------------------------------------------------------------------- */
- /* Utility macros and functions */
- #define INSERT_IN_DOUBLE_LINKED_LIST(backlink,object) \
- { \
- object->sibling_next=backlink; \
- object->sibling_prev_p=&(backlink); \
- backlink=object; \
- if (object->sibling_next) { \
- object->sibling_next->sibling_prev_p=&(object->sibling_next); \
- } \
- }
- #define EXTRACT_FROM_DOUBLE_LINKED_LIST(object) \
- { \
- if (object->sibling_next) { \
- object->sibling_next->sibling_prev_p=object->sibling_prev_p; \
- } \
- *(object->sibling_prev_p)=object->sibling_next; \
- }
- #define EXTRACT_FROM_DOUBLE_LINKED_LIST_MAYBE_NULL(object) \
- { \
- if (object->sibling_next) { \
- object->sibling_next->sibling_prev_p=object->sibling_prev_p; \
- } \
- if (object->sibling_prev_p) { \
- *(object->sibling_prev_p)=object->sibling_next; \
- } \
- }
- #define INSERT_IN_DOUBLE_LINKED_LIST_TXN(backlink,object) \
- { \
- object->sibling_next_txn=backlink; \
- object->sibling_prev_p_txn=&(backlink); \
- backlink=object; \
- if (object->sibling_next_txn) { \
- object->sibling_next_txn->sibling_prev_p_txn= \
- &(object->sibling_next_txn); \
- } \
- }
- #define EXTRACT_FROM_DOUBLE_LINKED_LIST_TXN(object) \
- { \
- if (object->sibling_next_txn) { \
- object->sibling_next_txn->sibling_prev_p_txn= \
- object->sibling_prev_p_txn; \
- } \
- *(object->sibling_prev_p_txn)=object->sibling_next_txn; \
- }
- #define RETURN_IF_ERR() \
- if (makeDBError(err)) { \
- return NULL; \
- }
- #define RETURN_NONE() Py_INCREF(Py_None); return Py_None;
- #define _CHECK_OBJECT_NOT_CLOSED(nonNull, pyErrObj, name) \
- if ((nonNull) == NULL) { \
- PyObject *errTuple = NULL; \
- errTuple = Py_BuildValue("(is)", 0, #name " object has been closed"); \
- if (errTuple) { \
- PyErr_SetObject((pyErrObj), errTuple); \
- Py_DECREF(errTuple); \
- } \
- return NULL; \
- }
- #define CHECK_DB_NOT_CLOSED(dbobj) \
- _CHECK_OBJECT_NOT_CLOSED(dbobj->db, DBError, DB)
- #define CHECK_ENV_NOT_CLOSED(env) \
- _CHECK_OBJECT_NOT_CLOSED(env->db_env, DBError, DBEnv)
- #define CHECK_CURSOR_NOT_CLOSED(curs) \
- _CHECK_OBJECT_NOT_CLOSED(curs->dbc, DBCursorClosedError, DBCursor)
- #if (DBVER >= 43)
- #define CHECK_SEQUENCE_NOT_CLOSED(curs) \
- _CHECK_OBJECT_NOT_CLOSED(curs->sequence, DBError, DBSequence)
- #endif
- #define CHECK_DBFLAG(mydb, flag) (((mydb)->flags & (flag)) || \
- (((mydb)->myenvobj != NULL) && ((mydb)->myenvobj->flags & (flag))))
- #define CLEAR_DBT(dbt) (memset(&(dbt), 0, sizeof(dbt)))
- #define FREE_DBT(dbt) if ((dbt.flags & (DB_DBT_MALLOC|DB_DBT_REALLOC)) && \
- dbt.data != NULL) { free(dbt.data); dbt.data = NULL; }
- static int makeDBError(int err);
- /* Return the access method type of the DBObject */
- static int _DB_get_type(DBObject* self)
- {
- DBTYPE type;
- int err;
- err = self->db->get_type(self->db, &type);
- if (makeDBError(err)) {
- return -1;
- }
- return type;
- }
- /* Create a DBT structure (containing key and data values) from Python
- strings. Returns 1 on success, 0 on an error. */
- static int make_dbt(PyObject* obj, DBT* dbt)
- {
- CLEAR_DBT(*dbt);
- if (obj == Py_None) {
- /* no need to do anything, the structure has already been zeroed */
- }
- else if (!PyArg_Parse(obj, "s#", &dbt->data, &dbt->size)) {
- PyErr_SetString(PyExc_TypeError,
- #if (PY_VERSION_HEX < 0x03000000)
- "Data values must be of type string or None.");
- #else
- "Data values must be of type bytes or None.");
- #endif
- return 0;
- }
- return 1;
- }
- /* 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.
- Caller MUST call FREE_DBT(key) when done. */
- static int
- make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags)
- {
- db_recno_t recno;
- int type;
- CLEAR_DBT(*key);
- if (keyobj == Py_None) {
- type = _DB_get_type(self);
- if (type == -1)
- return 0;
- if (type == DB_RECNO || type == DB_QUEUE) {
- PyErr_SetString(
- PyExc_TypeError,
- "None keys not allowed for Recno and Queue DB's");
- return 0;
- }
- /* no need to do anything, the structure has already been zeroed */
- }
- else if (PyBytes_Check(keyobj)) {
- /* verify access method type */
- type = _DB_get_type(self);
- if (type == -1)
- return 0;
- if (type == DB_RECNO || type == DB_QUEUE) {
- PyErr_SetString(
- PyExc_TypeError,
- #if (PY_VERSION_HEX < 0x03000000)
- "String keys not allowed for Recno and Queue DB's");
- #else
- "Bytes keys not allowed for Recno and Queue DB's");
- #endif
- return 0;
- }
- /*
- * NOTE(gps): I don't like doing a data copy here, it seems
- * wasteful. But without a clean way to tell FREE_DBT if it
- * should free key->data or not we have to. Other places in
- * the code check for DB_THREAD and forceably set DBT_MALLOC
- * when we otherwise would leave flags 0 to indicate that.
- */
- key->data = malloc(PyBytes_GET_SIZE(keyobj));
- if (key->data == NULL) {
- PyErr_SetString(PyExc_MemoryError, "Key memory allocation failed");
- return 0;
- }
- memcpy(key->data, PyBytes_AS_STRING(keyobj),
- PyBytes_GET_SIZE(keyobj));
- key->flags = DB_DBT_REALLOC;
- key->size = PyBytes_GET_SIZE(keyobj);
- }
- else if (NUMBER_Check(keyobj)) {
- /* verify access method type */
- type = _DB_get_type(self);
- if (type == -1)
- return 0;
- if (type == DB_BTREE && pflags != NULL) {
- /* if BTREE then an Integer key is allowed with the
- * DB_SET_RECNO flag */
- *pflags |= DB_SET_RECNO;
- }
- else if (type != DB_RECNO && type != DB_QUEUE) {
- PyErr_SetString(
- PyExc_TypeError,
- "Integer keys only allowed for Recno and Queue DB's");
- return 0;
- }
- /* Make a key out of the requested recno, use allocated space so DB
- * will be able to realloc room for the real key if needed. */
- recno = NUMBER_AsLong(keyobj);
- key->data = malloc(sizeof(db_recno_t));
- if (key->data == NULL) {
- PyErr_SetString(PyExc_MemoryError, "Key memory allocation failed");
- return 0;
- }
- key->ulen = key->size = sizeof(db_recno_t);
- memcpy(key->data, &recno, sizeof(db_recno_t));
- key->flags = DB_DBT_REALLOC;
- }
- else {
- PyErr_Format(PyExc_TypeError,
- #if (PY_VERSION_HEX < 0x03000000)
- "String or Integer object expected for key, %s found",
- #else
- "Bytes or Integer object expected for key, %s found",
- #endif
- Py_TYPE(keyobj)->tp_name);
- return 0;
- }
- return 1;
- }
- /* Add partial record access to an existing DBT data struct.
- If dlen and doff are set, then the DB_DBT_PARTIAL flag will be set
- and the data storage/retrieval will be done using dlen and doff. */
- static int add_partial_dbt(DBT* d, int dlen, int doff) {
- /* if neither were set we do nothing (-1 is the default value) */
- if ((dlen == -1) && (doff == -1)) {
- return 1;
- }
- if ((dlen < 0) || (doff < 0)) {
- PyErr_SetString(PyExc_TypeError, "dlen and doff must both be >= 0");
- return 0;
- }
- d->flags = d->flags | DB_DBT_PARTIAL;
- d->dlen = (unsigned int) dlen;
- d->doff = (unsigned int) doff;
- return 1;
- }
- /* a safe strcpy() without the zeroing behaviour and semantics of strncpy. */
- /* TODO: make this use the native libc strlcpy() when available (BSD) */
- unsigned int our_strlcpy(char* dest, const char* src, unsigned int n)
- {
- unsigned int srclen, copylen;
- srclen = strlen(src);
- if (n <= 0)
- return srclen;
- copylen = (srclen > n-1) ? n-1 : srclen;
- /* populate dest[0] thru dest[copylen-1] */
- memcpy(dest, src, copylen);
- /* guarantee null termination */
- dest[copylen] = 0;
- return srclen;
- }
- /* Callback used to save away more information about errors from the DB
- * library. */
- static char _db_errmsg[1024];
- #if (DBVER <= 42)
- static void _db_errorCallback(const char* prefix, char* msg)
- #else
- static void _db_errorCallback(const DB_ENV *db_env,
- const char* prefix, const char* msg)
- #endif
- {
- our_strlcpy(_db_errmsg, msg, sizeof(_db_errmsg));
- }
- /*
- ** We need these functions because some results
- ** are undefined if pointer is NULL. Some other
- ** give None instead of "".
- **
- ** This functions are static and will be
- ** -I hope- inlined.
- */
- static const char *DummyString = "This string is a simple placeholder";
- static PyObject *Build_PyString(const char *p,int s)
- {
- if (!p) {
- p=DummyString;
- assert(s==0);
- }
- return PyBytes_FromStringAndSize(p,s);
- }
- static PyObject *BuildValue_S(const void *p,int s)
- {
- if (!p) {
- p=DummyString;
- assert(s==0);
- }
- return PyBytes_FromStringAndSize(p, s);
- }
- static PyObject *BuildValue_SS(const void *p1,int s1,const void *p2,int s2)
- {
- PyObject *a, *b, *r;
- if (!p1) {
- p1=DummyString;
- assert(s1==0);
- }
- if (!p2) {
- p2=DummyString;
- assert(s2==0);
- }
- if (!(a = PyBytes_FromStringAndSize(p1, s1))) {
- return NULL;
- }
- if (!(b = PyBytes_FromStringAndSize(p2, s2))) {
- Py_DECREF(a);
- return NULL;
- }
- #if (PY_VERSION_HEX >= 0x02040000)
- r = PyTuple_Pack(2, a, b) ;
- #else
- r = Py_BuildValue("OO", a, b);
- #endif
- Py_DECREF(a);
- Py_DECREF(b);
- return r;
- }
- static PyObject *BuildValue_IS(int i,const void *p,int s)
- {
- PyObject *a, *r;
- if (!p) {
- p=DummyString;
- assert(s==0);
- }
- if (!(a = PyBytes_FromStringAndSize(p, s))) {
- return NULL;
- }
- r = Py_BuildValue("iO", i, a);
- Py_DECREF(a);
- return r;
- }
- static PyObject *BuildValue_LS(long l,const void *p,int s)
- {
- PyObject *a, *r;
- if (!p) {
- p=DummyString;
- assert(s==0);
- }
- if (!(a = PyBytes_FromStringAndSize(p, s))) {
- return NULL;
- }
- r = Py_BuildValue("lO", l, a);
- Py_DECREF(a);
- return r;
- }
- /* make a nice exception object to raise for errors. */
- static int makeDBError(int err)
- {
- char errTxt[2048]; /* really big, just in case... */
- PyObject *errObj = NULL;
- PyObject *errTuple = NULL;
- int exceptionRaised = 0;
- unsigned int bytes_left;
- switch (err) {
- case 0: /* successful, no error */ break;
- #if (DBVER < 41)
- case DB_INCOMPLETE:
- #if INCOMPLETE_IS_WARNING
- bytes_left = our_strlcpy(errTxt, db_strerror(err), sizeof(errTxt));
- /* Ensure that bytes_left never goes negative */
- if (_db_errmsg[0] && bytes_left < (sizeof(errTxt) - 4)) {
- bytes_left = sizeof(errTxt) - bytes_left - 4 - 1;
- assert(bytes_left >= 0);
- strcat(errTxt, " -- ");
- strncat(errTxt, _db_errmsg, bytes_left);
- }
- _db_errmsg[0] = 0;
- exceptionRaised = PyErr_Warn(PyExc_RuntimeWarning, errTxt);
- #else /* do an exception instead */
- errObj = DBIncompleteError;
- #endif
- break;
- #endif /* DBVER < 41 */
- case DB_KEYEMPTY: errObj = DBKeyEmptyError; break;
- case DB_KEYEXIST: errObj = DBKeyExistError; break;
- case DB_LOCK_DEADLOCK: errObj = DBLockDeadlockError; break;
- case DB_LOCK_NOTGRANTED: errObj = DBLockNotGrantedError; break;
- case DB_NOTFOUND: errObj = DBNotFoundError; break;
- case DB_OLD_VERSION: errObj = DBOldVersionError; break;
- case DB_RUNRECOVERY: errObj = DBRunRecoveryError; break;
- case DB_VERIFY_BAD: errObj = DBVerifyBadError; break;
- case DB_NOSERVER: errObj = DBNoServerError; break;
- case DB_NOSERVER_HOME: errObj = DBNoServerHomeError; break;
- case DB_NOSERVER_ID: errObj = DBNoServerIDError; break;
- case DB_PAGE_NOTFOUND: errObj = DBPageNotFoundError; break;
- case DB_SECONDARY_BAD: errObj = DBSecondaryBadError; break;
- case DB_BUFFER_SMALL: errObj = DBNoMemoryError; break;
- #if (DBVER >= 43)
- /* ENOMEM and DB_BUFFER_SMALL were one and the same until 4.3 */
- case ENOMEM: errObj = PyExc_MemoryError; break;
- #endif
- case EINVAL: errObj = DBInvalidArgError; break;
- case EACCES: errObj = DBAccessError; break;
- case ENOSPC: errObj = DBNoSpaceError; break;
- case EAGAIN: errObj = DBAgainError; break;
- case EBUSY : errObj = DBBusyError; break;
- case EEXIST: errObj = DBFileExistsError; break;
- case ENOENT: errObj = DBNoSuchFileError; break;
- case EPERM : errObj = DBPermissionsError; break;
- #if (DBVER >= 42)
- case DB_REP_HANDLE_DEAD : errObj = DBRepHandleDeadError; break;
- #endif
- case DB_REP_UNAVAIL : errObj = DBRepUnavailError; break;
- default: errObj = DBError; break;
- }
- if (errObj != NULL) {
- bytes_left = our_strlcpy(errTxt, db_strerror(err), sizeof(errTxt));
- /* Ensure that bytes_left never goes negative */
- if (_db_errmsg[0] && bytes_left < (sizeof(errTxt) - 4)) {
- bytes_left = sizeof(errTxt) - bytes_left - 4 - 1;
- assert(bytes_left >= 0);
- strcat(errTxt, " -- ");
- strncat(errTxt, _db_errmsg, bytes_left);
- }
- _db_errmsg[0] = 0;
- errTuple = Py_BuildValue("(is)", err, errTxt);
- if (errTuple == NULL) {
- Py_DECREF(errObj);
- return !0;
- }
- PyErr_SetObject(errObj, errTuple);
- Py_DECREF(errTuple);
- }
- return ((errObj != NULL) || exceptionRaised);
- }
- /* set a type exception */
- static void makeTypeError(char* expected, PyObject* found)
- {
- PyErr_Format(PyExc_TypeError, "Expected %s argument, %s found.",
- expected, Py_TYPE(found)->tp_name);
- }
- /* verify that an obj is either None or a DBTxn, and set the txn pointer */
- static int checkTxnObj(PyObject* txnobj, DB_TXN** txn)
- {
- if (txnobj == Py_None || txnobj == NULL) {
- *txn = NULL;
- return 1;
- }
- if (DBTxnObject_Check(txnobj)) {
- *txn = ((DBTxnObject*)txnobj)->txn;
- return 1;
- }
- else
- makeTypeError("DBTxn", txnobj);
- return 0;
- }
- /* Delete a key from a database
- Returns 0 on success, -1 on an error. */
- static int _DB_delete(DBObject* self, DB_TXN *txn, DBT *key, int flags)
- {
- int err;
- MYDB_BEGIN_ALLOW_THREADS;
- err = self->db->del(self->db, txn, key, 0);
- MYDB_END_ALLOW_THREADS;
- if (makeDBError(err)) {
- return -1;
- }
- self->haveStat = 0;
- return 0;
- }
- /* Store a key into a database
- Returns 0 on success, -1 on an error. */
- static int _DB_put(DBObject* self, DB_TXN *txn, DBT *key, DBT *data, int flags)
- {
- int err;
- MYDB_BEGIN_ALLOW_THREADS;
- err = self->db->put(self->db, txn, key, data, flags);
- MYDB_END_ALLOW_THREADS;
- if (makeDBError(err)) {
- return -1;
- }
- self->haveStat = 0;
- return 0;
- }
- /* Get a key/data pair from a cursor */
- static PyObject* _DBCursor_get(DBCursorObject* self, int extra_flags,
- PyObject *args, PyObject *kwargs, char *format)
- {
- int err;
- PyObject* retval = NULL;
- DBT key, data;
- int dlen = -1;
- int doff = -1;
- int flags = 0;
- static char* kwnames[] = { "flags", "dlen", "doff", NULL };
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, format, kwnames,
- &flags, &dlen, &doff))
- return NULL;
- CHECK_CURSOR_NOT_CLOSED(self);
- flags |= extra_flags;
- CLEAR_DBT(key);
- CLEAR_DBT(data);
- if (!add_partial_dbt(&data, dlen, doff))
- return NULL;
- MYDB_BEGIN_ALLOW_THREADS;
- err = _DBC_get(self->dbc, &key, &data, flags);
- MYDB_END_ALLOW_THREADS;
- if ((err == DB_NOTFOUND || err == DB_KEYEMPTY)
- && self->mydb->moduleFlags.getReturnsNone) {
- Py_INCREF(Py_None);
- retval = Py_None;
- }
- else if (makeDBError(err)) {
- retval = NULL;
- }
- else { /* otherwise, success! */
- /* if Recno or Queue, return the key as an Int */
- switch (_DB_get_type(self->mydb)) {
- case -1:
- retval = NULL;
- break;
- case DB_RECNO:
- case DB_QUEUE:
- retval = BuildValue_IS(*((db_recno_t*)key.data), data.data, data.size);
- break;
- case DB_HASH:
- case DB_BTREE:
- default:
- retval = BuildValue_SS(key.data, key.size, data.data, data.size);
- break;
- }
- }
- return retval;
- }
- /* add an integer to a dictionary using the given name as a key */
- static void _addIntToDict(PyObject* dict, char *name, int value)
- {
- PyObject* v = NUMBER_FromLong((long) value);
- if (!v || PyDict_SetItemString(dict, name, v))
- PyErr_Clear();
- Py_XDECREF(v);
- }
- /* The same, when the value is a time_t */
- static void _addTimeTToDict(PyObject* dict, char *name, time_t value)
- {
- PyObject* v;
- /* if the value fits in regular int, use that. */
- #ifdef PY_LONG_LONG
- if (sizeof(time_t) > sizeof(long))
- v = PyLong_FromLongLong((PY_LONG_LONG) value);
- else
- #endif
- v = NUMBER_FromLong((long) value);
- if (!v || PyDict_SetItemString(dict, name, v))
- PyErr_Clear();
- Py_XDECREF(v);
- }
- #if (DBVER >= 43)
- /* add an db_seq_t to a dictionary using the given name as a key */
- static void _addDb_seq_tToDict(PyObject* dict, char *name, db_seq_t value)
- {
- PyObject* v = PyLong_FromLongLong(value);
- if (!v || PyDict_SetItemString(dict, name, v))
- PyErr_Clear();
- Py_XDECREF(v);
- }
- #endif
- static void _addDB_lsnToDict(PyObject* dict, char *name, DB_LSN value)
- {
- PyObject *v = Py_BuildValue("(ll)",value.file,value.offset);
- if (!v || PyDict_SetItemString(dict, name, v))
- PyErr_Clear();
- Py_XDECREF(v);
- }
- /* --------------------------------------------------------------------- */
- /* Allocators and deallocators */
- static DBObject*
- newDBObject(DBEnvObject* arg, int flags)
- {
- DBObject* self;
- DB_ENV* db_env = NULL;
- int err;
- self = PyObject_New(DBObject, &DB_Type);
- if (self == NULL)
- return NULL;
- self->haveStat = 0;
- self->flags = 0;
- self->setflags = 0;
- self->myenvobj = NULL;
- self->db = NULL;
- self->children_cursors = NULL;
- #if (DBVER >=43)
- self->children_sequences = NULL;
- #endif
- self->associateCallback = NULL;
- self->btCompareCallback = NULL;
- self->primaryDBType = 0;
- Py_INCREF(Py_None);
- self->private_obj = Py_None;
- self->in_weakreflist = NULL;
- /* keep a reference to our python DBEnv object */
- if (arg) {
- Py_INCREF(arg);
- self->myenvobj = arg;
- db_env = arg->db_env;
- INSERT_IN_DOUBLE_LINKED_LIST(self->myenvobj->children_dbs,self);
- } else {
- self->sibling_prev_p=NULL;
- self->sibling_next=NULL;
- }
- self->txn=NULL;
- self->sibling_prev_p_txn=NULL;
- self->sibling_next_txn=NULL;
- if (self->myenvobj)
- self->moduleFlags = self->myenvobj->moduleFlags;
- else
- 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);
- if (self->db != NULL) {
- self->db->set_errcall(self->db, _db_errorCallback);
- self->db->app_private = (void*)self;
- }
- MYDB_END_ALLOW_THREADS;
- /* TODO add a weakref(self) to the self->myenvobj->open_child_weakrefs
- * list so that a DBEnv can refuse to close without aborting any open
- * DBTxns and closing any open DBs first. */
- if (makeDBError(err)) {
- if (self->myenvobj) {
- Py_DECREF(self->myenvobj);
- self->myenvobj = NULL;
- }
- Py_DECREF(self);
- self = NULL;
- }
- return self;
- }
- /* Forward declaration */
- static PyObject *DB_close_internal(DBObject* self, int flags, int do_not_close);
- static void
- DB_dealloc(DBObject* self)
- {
- PyObject *dummy;
- if (self->db != NULL) {
- dummy=DB_close_internal(self, 0, 0);
- /*
- ** Raising exceptions while doing
- ** garbage collection is a fatal error.
- */
- if (dummy)
- Py_DECREF(dummy);
- else
- PyErr_Clear();
- }
- if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *) self);
- }
- if (self->myenvobj) {
- Py_DECREF(self->myenvobj);
- self->myenvobj = NULL;
- }
- if (self->associateCallback != NULL) {
- Py_DECREF(self->associateCallback);
- self->associateCallback = NULL;
- }
- if (self->btCompareCallback != NULL) {
- Py_DECREF(self->btCompareCallback);
- self->btCompareCallback = NULL;
- }
- Py_DECREF(self->private_obj);
- PyObject_Del(self);
- }
- static DBCursorObject*
- newDBCursorObject(DBC* dbc, DBTxnObject *txn, DBObject* db)
- {
- DBCursorObject* self = PyObject_New(DBCursorObject, &DBCursor_Type);
- if (self == NULL)
- return NULL;
- self->dbc = dbc;
- self->mydb = db;
- INSERT_IN_DOUBLE_LINKED_LIST(self->mydb->children_cursors,self);
- if (txn && ((PyObject *)txn!=Py_None)) {
- INSERT_IN_DOUBLE_LINKED_LIST_TXN(txn->children_cursors,self);
- self->txn=txn;
- } else {
- self->txn=NULL;
- }
- self->in_weakreflist = NULL;
- Py_INCREF(self->mydb);
- return self;
- }
- /* Forward declaration */
- static PyObject *DBC_close_internal(DBCursorObject* self);
- static void
- DBCursor_dealloc(DBCursorObject* self)
- {
- PyObject *dummy;
- if (self->dbc != NULL) {
- dummy=DBC_close_internal(self);
- /*
- ** Raising exceptions while doing
- ** garbage collection is a fatal error.
- */
- if (dummy)
- Py_DECREF(dummy);
- else
- PyErr_Clear();
- }
- if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *) self);
- }
- Py_DECREF(self->mydb);
- PyObject_Del(self);
- }
- static DBEnvObject*
- newDBEnvObject(int flags)
- {
- int err;
- DBEnvObject* self = PyObject_New(DBEnvObject, &DBEnv_Type);
- if (self == NULL)
- return NULL;
- self->db_env = NULL;
- self->closed = 1;
- self->flags = flags;
- self->moduleFlags.getReturnsNone = DEFAULT_GET_RETURNS_NONE;
- self->moduleFlags.cursorSetReturnsNone = DEFAULT_CURSOR_SET_RETURNS_NONE;
- self->children_dbs = NULL;
- self->children_txns = NULL;
- Py_INCREF(Py_None);
- self->private_obj = Py_None;
- Py_INCREF(Py_None);
- self->rep_transport = Py_None;
- self->in_weakreflist = NULL;
- self->event_notifyCallback = NULL;
- MYDB_BEGIN_ALLOW_THREADS;
- err = db_env_create(&self->db_env, flags);
- MYDB_END_ALLOW_THREADS;
- if (makeDBError(err)) {
- Py_DECREF(self);
- self = NULL;
- }
- else {
- self->db_env->set_errcall(self->db_env, _db_errorCallback);
- self->db_env->app_private = self;
- }
- return self;
- }
- /* Forward declaration */
- static PyObject *DBEnv_close_internal(DBEnvObject* self, int flags);
- static void
- DBEnv_dealloc(DBEnvObject* self)
- {
- PyObject *dummy;
- if (self->db_env) {
- dummy=DBEnv_close_internal(self, 0);
- /*
- ** Raising exceptions while doing
- ** garbage collection is a fatal error.
- */
- if (dummy)
- Py_DECREF(dummy);
- else
- PyErr_Clear();
- }
- Py_XDECREF(self->event_notifyCallback);
- self->event_notifyCallback = NULL;
- if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *) self);
- }
- Py_DECREF(self->private_obj);
- Py_DECREF(self->rep_transport);
- PyObject_Del(self);
- }
- static DBTxnObject*
- newDBTxnObject(DBEnvObject* myenv, DBTxnObject *parent, DB_TXN *txn, int flags)
- {
- int err;
- DB_TXN *parent_txn = NULL;
- DBTxnObject* self = PyObject_New(DBTxnObject, &DBTxn_Type);
- if (self == NULL)
- return NULL;
- self->in_weakreflist = NULL;
- self->children_txns = NULL;
- self->children_dbs = NULL;
- self->children_cursors = NULL;
- self->children_sequences = NULL;
- self->flag_prepare = 0;
- self->parent_txn = NULL;
- self->env = NULL;
- if (parent && ((PyObject *)parent!=Py_None)) {
- parent_txn = parent->txn;
- }
- if (txn) {
- self->txn = txn;
- } else {
- MYDB_BEGIN_ALLOW_THREADS;
- err = myenv->db_env->txn_begin(myenv->db_env, parent_txn, &(self->txn), flags);
- MYDB_END_ALLOW_THREADS;
- if (makeDBError(err)) {
- Py_DECREF(self);
- return NULL;
- }
- }
- /* Can't use 'parent' because could be 'parent==Py_None' */
- if (parent_txn) {
- self->parent_txn = parent;
- Py_INCREF(parent);
- self->env = NULL;
- INSERT_IN_DOUBLE_LINKED_LIST(parent->children_txns, self);
- } else {
- self->parent_txn = NULL;
- Py_INCREF(myenv);
- self->env = myenv;
- INSERT_IN_DOUBLE_LINKED_LIST(myenv->children_txns, self);
- }
- return self;
- }
- /* Forward declaration */
- static PyObject *
- DBTxn_abort_discard_internal(DBTxnObject* self, int discard);
- static void
- DBTxn_dealloc(DBTxnObject* self)
- {
- PyObject *dummy;
- if (self->txn) {
- int flag_prepare = self->flag_prepare;
- dummy=DBTxn_abort_discard_internal(self,0);
- /*
- ** Raising exceptions while doing
- ** garbage collection is a fatal error.
- */
- if (dummy)
- Py_DECREF(dummy);
- else
- PyErr_Clear();
- if (!flag_prepare) {
- PyErr_Warn(PyExc_RuntimeWarning,
- "DBTxn aborted in destructor. No prior commit() or abort().");
- }
- }
- if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *) self);
- }
- if (self->env) {
- Py_DECREF(self->env);
- } else {
- Py_DECREF(self->parent_txn);
- }
- PyObject_Del(self);
- }
- static DBLockObject*
- newDBLockObject(DBEnvObject* myenv, u_int32_t locker, DBT* obj,
- db_lockmode_t lock_mode, int flags)
- {
- int err;
- DBLockObject* self = PyObject_New(DBLockObject, &DBLock_Type);
- if (self == NULL)
- return NULL;
- self->in_weakreflist = NULL;
- MYDB_BEGIN_ALLOW_THREADS;
- err = myenv->db_env->lock_get(myenv->db_env, locker, flags, obj, lock_mode,
- &self->lock);
- MYDB_END_ALLOW_THREADS;
- if (makeDBError(err)) {
- Py_DECREF(self);
- self = NULL;
- }
- return self;
- }
- static void
- DBLock_dealloc(DBLockObject* self)
- {
- if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *) self);
- }
- /* TODO: is this lock held? should we release it? */
- PyObject_Del(self);
- }
- #if (DBVER >= 43)
- static DBSequenceObject*
- newDBSequenceObject(DBObject* mydb, int flags)
- {
- int err;
- DBSequenceObject* self = PyObject_New(DBSequenceObject, &DBSequence_Type);
- if (self == NULL)
- return NULL;
- Py_INCREF(mydb);
- self->mydb = mydb;
- INSERT_IN_DOUBLE_LINKED_LIST(self->mydb->children_sequences,self);
- self->txn = NULL;
- self->in_weakreflist = NULL;
- MYDB_BEGIN_ALLOW_THREADS;
- err = db_sequence_create(&self->sequence, self->mydb->db, flags);
- MYDB_END_ALLOW_THREADS;
- if (makeDBError(err)) {
- Py_DECREF(self);
- self = NULL;
- }
- return self;
- }
- /* Forward declaration */
- static PyObject
- *DBSequence_close_internal(DBSequenceObject* self, int flags, int do_not_close);
- static void
- DBSequence_dealloc(DBSequenceObject* self)
- {
- PyObject *dummy;
- if (self->sequence != NULL) {
- dummy=DBSequence_close_internal(self,0,0);
- /*
- ** Raising exceptions while doing
- ** garbage collection is a fatal error.
- */
- if (dummy)
- Py_DECREF(dummy);
- else
- PyErr_Clear();
- }
- if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *) self);
- }
- Py_DECREF(self->mydb);
- PyObject_Del(self);
- }
- #endif
- /* --------------------------------------------------------------------- */
- /* DB methods */
- static PyObject*
- DB_append(DBObject* self, PyObject* args, PyObject* kwargs)
- {
- PyObject* txnobj = NULL;
- PyObject* dataobj;
- db_recno_t recno;
- DBT key, data;
- DB_TXN *txn = NULL;
- static char* kwnames[] = { "data", "txn", NULL };
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:append", kwnames,
- &dataobj, &txnobj))
- return NULL;
- CHECK_DB_NOT_CLOSED(self);
- /* make a dummy key out of a recno */
- recno = 0;
- CLEAR_DBT(key);
- key.data = &recno;
- key.size = sizeof(recno);
- key.ulen = key.size;
- key.flags = DB_DBT_USERMEM;
- if (!make_dbt(dataobj, &data)) return NULL;
- if (!checkTxnObj(txnobj, &txn)) return NULL;
- if (-1 == _DB_put(self, txn, &key, &data, DB_APPEND))
- return NULL;
- return NUMBER_FromLong(recno);
- }
- static int
- _db_associateCallback(DB* db, const DBT* priKey, const DBT* priData,
- DBT* secKey)
- {
- int retval = DB_DONOTINDEX;
- DBObject* secondaryDB = (DBObject*)db->app_private;
- PyObject* callback = secondaryDB->associateCallback;
- int type = secondaryDB->primaryDBType;
- PyObject* args;
- PyObject* result = NULL;
- if (callback != NULL) {
- MYDB_BEGIN_BLOCK_THREADS;
- if (type == DB_RECNO || type == DB_QUEUE)
- args = BuildValue_LS(*((db_recno_t*)priKey->data), priData->data, priData->size);
- else
- args = BuildValue_SS(priKey->data, priKey->size, priData->data, priData->size);
- if (args != NULL) {
- result = PyEval_CallObject(callback, args);
- }
- if (args == NULL || result == NULL) {
- PyErr_Print();
- }
- else if (result == Py_None) {
- retval = DB_DONOTINDEX;
- }
- else if (NUMBER_Check(result)) {
- retval = NUMBER_AsLong(result);
- }
- else if (PyBytes_Check(result)) {
- char* data;
- Py_ssize_t size;
- CLEAR_DBT(*secKey);
- PyBytes_AsStringAndSize(result, &data, &size);
- secKey->flags = DB_DBT_APPMALLOC; /* DB will free */
- secKey->data = malloc(size); /* TODO, check this */
- if (secKey->data) {
- memcpy(secKey->data, data, size);
- secKey->size = size;
- retval = 0;
- }
- else {
- PyErr_SetString(PyExc_MemoryError,
- "malloc failed in _db_associateCallback");
- PyErr_Print();
- }
- }
- else {
- PyErr_SetString(
- PyExc_TypeError,
- "DB associate callback should return DB_DONOTINDEX or string.");
- PyErr_Print();
- }
- Py_XDECREF(args);
- Py_XDECREF(result);
- MYDB_END_BLOCK_THREADS;
- }
- return retval;
- }
- static PyObject*
- DB_associate(DBObject* self, PyObject* args, PyObject* kwargs)
- {
- int err, flags=0;
- DBObject* secondaryDB;
- PyObject* callback;
- #if (DBVER >= 41)
- PyObject *txnobj = NULL;
- DB_TXN *txn = NULL;
- static char* kwnames[] = {"secondaryDB", "callback", "flags", "txn",
- NULL};
- #else
- static char* kwnames[] = {"secondaryDB", "callback", "flags", NULL};
- #endif
- #if (DBVER >= 41)
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iO:associate", kwnames,
- &secondaryDB, &callback, &flags,
- &txnobj)) {
- #else
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i:associate", kwnames,
- &secondaryDB, &callback, &flags)) {
- #endif
- return NULL;
- }
- #if (DBVER >= 41)
- if (!checkTxnObj(txnobj, &txn)) return NULL;
- #endif
- CHECK_DB_NOT_CLOSED(self);
- if (!DBObject_Check(secondaryDB)) {
- makeTypeError("DB", (PyObject*)secondaryDB);
- return NULL;
- }
- CHECK_DB_NOT_CLOSED(secondaryDB);
- if (callback == Py_None) {
- callback = NULL;
- }
- else if (!PyCallable_Check(callback)) {
- makeTypeError("Callable", callback);
- return NULL;
- }
- /* Save a reference to the callback in the secondary DB. */
- Py_XDECREF(secondaryDB->associateCallback);
- Py_XINCREF(callback);
- secondaryDB->associateCallback = callback;
- secondaryDB->primaryDBType = _DB_get_type(self);
- /* PyEval_InitThreads is called here due to a quirk in python 1.5
- * - 2.2.1 (at least) according to Russell Williamson <merel@wt.net>:
- * The global interepreter lock is not initialized until the first
- * thread is created using thread.start_new_thread() or fork() is
- * called. that would cause the ALLOW_THREADS here to segfault due
- * to a null pointer reference if no threads or child processes
- * have been created. This works around that and is a no-op if
- * threads have already been initialized.
- * (see pybsddb-users mailing list post on 2002-08-07)
- */
- #ifdef WITH_THREAD
- PyEval_InitThreads();
- #endif
- MYDB_BEGIN_ALLOW_THREADS;
- #if (DBVER >= 41)
- err = self->db->associate(self->db,
- txn,
- secondaryDB->db,
- _db_associateCallback,
- flags);
- #else
- err = self->db->associate(self->db,
- secondaryDB->db,
- _db_associateCallback,
- flags);
- #endif
- MYDB_END_ALLOW_THREADS;
- if (err) {
- Py_XDECREF(secondaryDB->associateCallback);
- secondaryDB->associateCallback = NULL;
- secondaryDB->primaryDBType = 0;
- }
- RETURN_IF_ERR();
- RETURN_NONE();
- }
- static PyObject*
- DB_close_internal(DBObject* self, int flags, int do_not_close)
- {
- PyObject *dummy;
- int err = 0;
- if (self->db != NULL) {
- /* Can be NULL if db is not in an environment */
- EXTRACT_FROM_DOUBLE_LINKED_LIST_MAYBE_NULL(self);
- if (self->txn) {
- EXTRACT_FROM_DOUBLE_LINKED_LIST_TXN(self);
- self->txn=NULL;
- }
- while(self->children_cursors) {
- dummy=DBC_close_internal(self->children_cursors);
- Py_XDECREF(dummy);
- }
- #if (DBVER >= 43)
- while(self->children_sequences) {
- dummy=DBSequence_close_internal(self->children_sequences,0,0);
- Py_XDECREF(dummy);
- }
- #endif
- /*
- ** "do_not_close" is used to dispose all related objects in the
- ** tree, without actually releasing the "root" object.
- ** This is done, for example, because function calls like
- ** "DB.verify()" implicitly close the underlying handle. So
- ** the handle doesn't need to be closed, but related objects
- ** must be cleaned up.
- */
- if (!do_not_close) {
- MYDB_BEGIN_ALLOW_THREADS;
- err = self->db->close(self->db, flags);
- MYDB_END_ALLOW_THREADS;
- self->db = NULL;
- }
- RETURN_IF_ERR();
- }
- RETURN_NONE();
- }
- static PyObject*
- DB_close(DBObject* self, PyObject* args)
- {
- int flags=0;
- if (!PyArg_ParseTuple(args,"|i:close", &flags))
- return NULL;
- return DB_close_internal(self, flags, 0);
- }
- static PyObject*
- _DB_consume(DBObject* self, PyObject* args, PyObject* kwargs, int consume_flag)
- {
- int err, flags=0, type;
- PyObject* txnobj = NULL;
- PyObject* retval = NULL;
- DBT key, data;
- DB_TXN *txn = NULL;
- static char* kwnames[] = { "txn", "flags", NULL };
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:consume", kwnames,
- &txnobj, &flags))
- return NULL;
- CHECK_DB_NOT_CLOSED(self);
- type = _DB_get_type(self);
- if (type == -1)
- return NULL;
- if (type != DB_QUEUE) {
- PyErr_SetString(PyExc_TypeError,
- "Consume methods only allowed for Queue DB's");
- return NULL;
- }
- if (!checkTxnObj(txnobj, &txn))
- return NULL;
- CLEAR_DBT(key);
- CLEAR_DBT(data);
- if (CHECK_DBFLAG(self, DB_THREAD)) {
- /* Tell Berkeley DB to malloc the return value (thread safe) */
- data.flags = DB_DBT_MALLOC;
- key.flags = DB_DBT_MALLOC;
- }
- MYDB_BEGIN_ALLOW_THREADS;
- err = self->db->get(self->db, txn, &key, &data, flags|consume_flag);
- MYDB_END_ALLOW_THREADS;
- if ((err == DB_NOTFOUND || err == DB_KEYEMPTY)
- && self->moduleFlags.getReturnsNone) {
- err = 0;
- Py_INCREF(Py_None);
- retval = Py_None;
- }
- else if (!err) {
- retval = BuildValue_SS(key.data, key.size, data.data, data.size);
- FREE_DBT(key);
- FREE_DBT(data);
- }
- RETURN_IF_ERR();
- return retval;
- }
- static PyObject*
- DB_consume(DBObject* self, PyObject* args, PyObject* kwargs, int consume_flag)
- {
- return _DB_consume(self, args, kwargs, DB_CONSUME);
- }
- static PyObject*
- DB_consume_wait(DBObject* self, PyObject* args, PyObject* kwargs,
- int consume_flag)
- {
- return _DB_consume(self, args, kwargs, DB_CONSUME_WAIT);
- }
- static PyObject*
- DB_cursor(DBObject* self, PyObject* args, PyObject* kwargs)
- {
- int err, flags=0;
- DBC* dbc;
- PyObject* txnobj = NULL;
- DB_TXN *txn = NULL;
- static char* kwnames[] = { "txn", "flags", NULL };
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:cursor", kwnames,…