6047 lines · 162.3 KB · C
RawDownload
/*----------------------------------------------------------------------  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 BerkeleyDB 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 <greg@electricrain.com> 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@electricrain.com> is once again the maintainer. * * 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>#include <db.h> /* --------------------------------------------------------------------- *//* Various macro definitions */ /* 40 = 4.0, 33 = 3.3; this will break if the second number is > 9 */#define DBVER (DB_VERSION_MAJOR * 10 + DB_VERSION_MINOR)#if DB_VERSION_MINOR > 9#error "eek! DBVER can't handle minor versions > 9"#endif #define PY_BSDDB_VERSION "4.4.5.2"static char *rcs_id = "$Id: _bsddb.c 53254 2007-01-05 02:09:06Z gregory.p.smith $";  #if (PY_VERSION_HEX < 0x02050000)typedef int Py_ssize_t;#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 */#if (DBVER >= 33)static PyObject* DBPageNotFoundError;   /* DB_PAGE_NOTFOUND */static PyObject* DBSecondaryBadError;   /* DB_SECONDARY_BAD */#endif #if !INCOMPLETE_IS_WARNINGstatic 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 < 43)#define	DB_BUFFER_SMALL		ENOMEM#endif  /* --------------------------------------------------------------------- *//* Structure definitions */ #if PYTHON_API_VERSION >= 1010       /* python >= 2.1 support weak references */#define HAVE_WEAKREF#else#undef HAVE_WEAKREF#endif /* if Python >= 2.1 better support warnings */#if PYTHON_API_VERSION >= 1010#define HAVE_WARNINGS#else#undef HAVE_WARNINGS#endif #if PYTHON_API_VERSION <= 1007    /* 1.5 compatibility */#define PyObject_New PyObject_NEW#define PyObject_Del PyMem_DEL#endif struct behaviourFlags {    /* What is the default behaviour when DB->get or DBCursor->get returns a       DB_NOTFOUND || DB_KEYEMPTY 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 || DB_KEYEMPTY  error?  Return None or raise? */    unsigned int cursorSetReturnsNone : 1;}; #define DEFAULT_GET_RETURNS_NONE                1#define DEFAULT_CURSOR_SET_RETURNS_NONE         1   /* 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;    struct behaviourFlags moduleFlags;#ifdef HAVE_WEAKREF    PyObject        *in_weakreflist; /* List of weak references */#endif} DBEnvObject;  typedef struct {    PyObject_HEAD    DB*             db;    DBEnvObject*    myenvobj;  /* PyObject containing the DB_ENV */    u_int32_t       flags;     /* saved flags from open() */    u_int32_t       setflags;  /* saved flags from set_flags() */    int             haveStat;    struct behaviourFlags moduleFlags;#if (DBVER >= 33)    PyObject*       associateCallback;    PyObject*       btCompareCallback;    int             primaryDBType;#endif#ifdef HAVE_WEAKREF    PyObject        *in_weakreflist; /* List of weak references */#endif} DBObject;  typedef struct {    PyObject_HEAD    DBC*            dbc;    DBObject*       mydb;#ifdef HAVE_WEAKREF    PyObject        *in_weakreflist; /* List of weak references */#endif} DBCursorObject;  typedef struct {    PyObject_HEAD    DB_TXN*         txn;    PyObject        *env;#ifdef HAVE_WEAKREF    PyObject        *in_weakreflist; /* List of weak references */#endif} DBTxnObject;  typedef struct {    PyObject_HEAD    DB_LOCK         lock;#ifdef HAVE_WEAKREF    PyObject        *in_weakreflist; /* List of weak references */#endif} DBLockObject; #if (DBVER >= 43)typedef struct {    PyObject_HEAD    DB_SEQUENCE*     sequence;    DBObject*        mydb;#ifdef HAVE_WEAKREF    PyObject        *in_weakreflist; /* List of weak references */#endif} DBSequenceObject;staticforward PyTypeObject DBSequence_Type;#endif staticforward PyTypeObject DB_Type, DBCursor_Type, DBEnv_Type, DBTxn_Type, DBLock_Type; #define DBObject_Check(v)           ((v)->ob_type == &DB_Type)#define DBCursorObject_Check(v)     ((v)->ob_type == &DBCursor_Type)#define DBEnvObject_Check(v)        ((v)->ob_type == &DBEnv_Type)#define DBTxnObject_Check(v)        ((v)->ob_type == &DBTxn_Type)#define DBLockObject_Check(v)       ((v)->ob_type == &DBLock_Type)#if (DBVER >= 43)#define DBSequenceObject_Check(v)   ((v)->ob_type == &DBSequence_Type)#endif  /* --------------------------------------------------------------------- *//* Utility macros and functions */ #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"); \        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){#if (DBVER >= 33)    DBTYPE type;    int err;    err = self->db->get_type(self->db, &type);    if (makeDBError(err)) {        return -1;    }    return type;#else    return self->db->get_type(self->db);#endif}  /* 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,                        "Data values must be of type string or None.");        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 intmake_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 (PyString_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,                "String keys not allowed for Recno and Queue DB's");            return 0;        }         key->data = PyString_AS_STRING(keyobj);        key->size = PyString_GET_SIZE(keyobj);    }     else if (PyInt_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 = PyInt_AS_LONG(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,                     "String or Integer object expected for key, %s found",                     keyobj->ob_type->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;

Lines 1–500 of 6047.