diff -r ffa851df0825 -r 2fb8b9db1c86 symbian-qemu-0.9.1-12/python-2.6.1/Modules/_bsddb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/symbian-qemu-0.9.1-12/python-2.6.1/Modules/_bsddb.c Fri Jul 31 15:01:17 2009 +0100 @@ -0,0 +1,7581 @@ +/*---------------------------------------------------------------------- + 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 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 was once again the maintainer. + * + * Since January 2008, new maintainer is Jesus Cea . + * 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 /* for offsetof() */ +#include + +#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 : + * 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, + &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->cursor(self->db, txn, &dbc, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return (PyObject*) newDBCursorObject(dbc, (DBTxnObject *)txnobj, self); +} + + +static PyObject* +DB_delete(DBObject* self, PyObject* args, PyObject* kwargs) +{ + PyObject* txnobj = NULL; + int flags = 0; + PyObject* keyobj; + DBT key; + DB_TXN *txn = NULL; + static char* kwnames[] = { "key", "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oi:delete", kwnames, + &keyobj, &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) { + FREE_DBT(key); + return NULL; + } + + if (-1 == _DB_delete(self, txn, &key, 0)) { + FREE_DBT(key); + return NULL; + } + + FREE_DBT(key); + RETURN_NONE(); +} + + +static PyObject* +DB_fd(DBObject* self) +{ + int err, the_fd; + + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->fd(self->db, &the_fd); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(the_fd); +} + + +static PyObject* +DB_get(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + PyObject* dfltobj = NULL; + PyObject* retval = NULL; + int dlen = -1; + int doff = -1; + DBT key, data; + DB_TXN *txn = NULL; + static char* kwnames[] = {"key", "default", "txn", "flags", "dlen", + "doff", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOiii:get", kwnames, + &keyobj, &dfltobj, &txnobj, &flags, &dlen, + &doff)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, &flags)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) { + FREE_DBT(key); + return NULL; + } + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell Berkeley DB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + } + if (!add_partial_dbt(&data, dlen, doff)) { + FREE_DBT(key); + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + + if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) && (dfltobj != NULL)) { + err = 0; + Py_INCREF(dfltobj); + retval = dfltobj; + } + else if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) + && self->moduleFlags.getReturnsNone) { + err = 0; + Py_INCREF(Py_None); + retval = Py_None; + } + else if (!err) { + if (flags & DB_SET_RECNO) /* return both key and data */ + retval = BuildValue_SS(key.data, key.size, data.data, data.size); + else /* return just the data */ + retval = Build_PyString(data.data, data.size); + FREE_DBT(data); + } + FREE_DBT(key); + + RETURN_IF_ERR(); + return retval; +} + +static PyObject* +DB_pget(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + PyObject* dfltobj = NULL; + PyObject* retval = NULL; + int dlen = -1; + int doff = -1; + DBT key, pkey, data; + DB_TXN *txn = NULL; + static char* kwnames[] = {"key", "default", "txn", "flags", "dlen", + "doff", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOiii:pget", kwnames, + &keyobj, &dfltobj, &txnobj, &flags, &dlen, + &doff)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, &flags)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) { + FREE_DBT(key); + return NULL; + } + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell Berkeley DB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + } + if (!add_partial_dbt(&data, dlen, doff)) { + FREE_DBT(key); + return NULL; + } + + CLEAR_DBT(pkey); + pkey.flags = DB_DBT_MALLOC; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->pget(self->db, txn, &key, &pkey, &data, flags); + MYDB_END_ALLOW_THREADS; + + if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) && (dfltobj != NULL)) { + err = 0; + Py_INCREF(dfltobj); + retval = dfltobj; + } + else if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) + && self->moduleFlags.getReturnsNone) { + err = 0; + Py_INCREF(Py_None); + retval = Py_None; + } + else if (!err) { + PyObject *pkeyObj; + PyObject *dataObj; + dataObj = Build_PyString(data.data, data.size); + + if (self->primaryDBType == DB_RECNO || + self->primaryDBType == DB_QUEUE) + pkeyObj = NUMBER_FromLong(*(int *)pkey.data); + else + pkeyObj = Build_PyString(pkey.data, pkey.size); + + if (flags & DB_SET_RECNO) /* return key , pkey and data */ + { + PyObject *keyObj; + int type = _DB_get_type(self); + if (type == DB_RECNO || type == DB_QUEUE) + keyObj = NUMBER_FromLong(*(int *)key.data); + else + keyObj = Build_PyString(key.data, key.size); +#if (PY_VERSION_HEX >= 0x02040000) + retval = PyTuple_Pack(3, keyObj, pkeyObj, dataObj); +#else + retval = Py_BuildValue("OOO", keyObj, pkeyObj, dataObj); +#endif + Py_DECREF(keyObj); + } + else /* return just the pkey and data */ + { +#if (PY_VERSION_HEX >= 0x02040000) + retval = PyTuple_Pack(2, pkeyObj, dataObj); +#else + retval = Py_BuildValue("OO", pkeyObj, dataObj); +#endif + } + Py_DECREF(dataObj); + Py_DECREF(pkeyObj); + FREE_DBT(pkey); + FREE_DBT(data); + } + FREE_DBT(key); + + RETURN_IF_ERR(); + return retval; +} + + +/* Return size of entry */ +static PyObject* +DB_get_size(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + PyObject* retval = NULL; + DBT key, data; + DB_TXN *txn = NULL; + static char* kwnames[] = { "key", "txn", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:get_size", kwnames, + &keyobj, &txnobj)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, &flags)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) { + FREE_DBT(key); + return NULL; + } + CLEAR_DBT(data); + + /* We don't allocate any memory, forcing a DB_BUFFER_SMALL error and + thus getting the record size. */ + data.flags = DB_DBT_USERMEM; + data.ulen = 0; + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + if (err == DB_BUFFER_SMALL) { + retval = NUMBER_FromLong((long)data.size); + err = 0; + } + + FREE_DBT(key); + FREE_DBT(data); + RETURN_IF_ERR(); + return retval; +} + + +static PyObject* +DB_get_both(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + PyObject* dataobj; + PyObject* retval = NULL; + DBT key, data; + void *orig_data; + DB_TXN *txn = NULL; + static char* kwnames[] = { "key", "data", "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|Oi:get_both", kwnames, + &keyobj, &dataobj, &txnobj, &flags)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + if ( !make_dbt(dataobj, &data) || + !checkTxnObj(txnobj, &txn) ) + { + FREE_DBT(key); + return NULL; + } + + flags |= DB_GET_BOTH; + orig_data = data.data; + + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell Berkeley DB to malloc the return value (thread safe) */ + /* XXX(nnorwitz): At least 4.4.20 and 4.5.20 require this flag. */ + data.flags = DB_DBT_MALLOC; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, flags); + 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) { + /* XXX(nnorwitz): can we do: retval = dataobj; Py_INCREF(retval); */ + retval = Build_PyString(data.data, data.size); + + /* Even though the flags require DB_DBT_MALLOC, data is not always + allocated. 4.4: allocated, 4.5: *not* allocated. :-( */ + if (data.data != orig_data) + FREE_DBT(data); + } + + FREE_DBT(key); + RETURN_IF_ERR(); + return retval; +} + + +static PyObject* +DB_get_byteswapped(DBObject* self) +{ + int err = 0; + int retval = -1; + + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get_byteswapped(self->db, &retval); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(retval); +} + + +static PyObject* +DB_get_type(DBObject* self) +{ + int type; + + CHECK_DB_NOT_CLOSED(self); + + type = _DB_get_type(self); + if (type == -1) + return NULL; + return NUMBER_FromLong(type); +} + + +static PyObject* +DB_join(DBObject* self, PyObject* args) +{ + int err, flags=0; + int length, x; + PyObject* cursorsObj; + DBC** cursors; + DBC* dbc; + + if (!PyArg_ParseTuple(args,"O|i:join", &cursorsObj, &flags)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + + if (!PySequence_Check(cursorsObj)) { + PyErr_SetString(PyExc_TypeError, + "Sequence of DBCursor objects expected"); + return NULL; + } + + length = PyObject_Length(cursorsObj); + cursors = malloc((length+1) * sizeof(DBC*)); + if (!cursors) { + PyErr_NoMemory(); + return NULL; + } + + cursors[length] = NULL; + for (x=0; xdbc; + Py_DECREF(item); + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->join(self->db, cursors, &dbc, flags); + MYDB_END_ALLOW_THREADS; + 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, NULL, self); +} + + +static PyObject* +DB_key_range(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + DBT key; + DB_TXN *txn = NULL; + DB_KEY_RANGE range; + static char* kwnames[] = { "key", "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oi:key_range", kwnames, + &keyobj, &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!make_dbt(keyobj, &key)) + /* BTree only, don't need to allow for an int key */ + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->key_range(self->db, txn, &key, &range, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + return Py_BuildValue("ddd", range.less, range.equal, range.greater); +} + + +static PyObject* +DB_open(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, type = DB_UNKNOWN, flags=0, mode=0660; + char* filename = NULL; + char* dbname = NULL; +#if (DBVER >= 41) + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + /* with dbname */ + static char* kwnames[] = { + "filename", "dbname", "dbtype", "flags", "mode", "txn", NULL}; + /* without dbname */ + static char* kwnames_basic[] = { + "filename", "dbtype", "flags", "mode", "txn", NULL}; +#else + /* with dbname */ + static char* kwnames[] = { + "filename", "dbname", "dbtype", "flags", "mode", NULL}; + /* without dbname */ + static char* kwnames_basic[] = { + "filename", "dbtype", "flags", "mode", NULL}; +#endif + +#if (DBVER >= 41) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|ziiiO:open", kwnames, + &filename, &dbname, &type, &flags, &mode, + &txnobj)) +#else + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|ziii:open", kwnames, + &filename, &dbname, &type, &flags, + &mode)) +#endif + { + PyErr_Clear(); + type = DB_UNKNOWN; flags = 0; mode = 0660; + filename = NULL; dbname = NULL; +#if (DBVER >= 41) + if (!PyArg_ParseTupleAndKeywords(args, kwargs,"z|iiiO:open", + kwnames_basic, + &filename, &type, &flags, &mode, + &txnobj)) + return NULL; +#else + if (!PyArg_ParseTupleAndKeywords(args, kwargs,"z|iii:open", + kwnames_basic, + &filename, &type, &flags, &mode)) + return NULL; +#endif + } + +#if (DBVER >= 41) + if (!checkTxnObj(txnobj, &txn)) return NULL; +#endif + + if (NULL == self->db) { + PyObject *t = Py_BuildValue("(is)", 0, + "Cannot call open() twice for DB object"); + if (t) { + PyErr_SetObject(DBError, t); + Py_DECREF(t); + } + return NULL; + } + +#if (DBVER >= 41) + if (txn) { /* Can't use 'txnobj' because could be 'txnobj==Py_None' */ + INSERT_IN_DOUBLE_LINKED_LIST_TXN(((DBTxnObject *)txnobj)->children_dbs,self); + self->txn=(DBTxnObject *)txnobj; + } else { + self->txn=NULL; + } +#else + self->txn=NULL; +#endif + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 41) + err = self->db->open(self->db, txn, filename, dbname, type, flags, mode); +#else + err = self->db->open(self->db, filename, dbname, type, flags, mode); +#endif + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + PyObject *dummy; + + dummy=DB_close_internal(self, 0, 0); + Py_XDECREF(dummy); + return NULL; + } + +#if (DBVER >= 42) + self->db->get_flags(self->db, &self->setflags); +#endif + + self->flags = flags; + + RETURN_NONE(); +} + + +static PyObject* +DB_put(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int flags=0; + PyObject* txnobj = NULL; + int dlen = -1; + int doff = -1; + PyObject* keyobj, *dataobj, *retval; + DBT key, data; + DB_TXN *txn = NULL; + static char* kwnames[] = { "key", "data", "txn", "flags", "dlen", + "doff", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|Oiii:put", kwnames, + &keyobj, &dataobj, &txnobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + if ( !make_dbt(dataobj, &data) || + !add_partial_dbt(&data, dlen, doff) || + !checkTxnObj(txnobj, &txn) ) + { + FREE_DBT(key); + return NULL; + } + + if (-1 == _DB_put(self, txn, &key, &data, flags)) { + FREE_DBT(key); + return NULL; + } + + if (flags & DB_APPEND) + retval = NUMBER_FromLong(*((db_recno_t*)key.data)); + else { + retval = Py_None; + Py_INCREF(retval); + } + FREE_DBT(key); + return retval; +} + + + +static PyObject* +DB_remove(DBObject* self, PyObject* args, PyObject* kwargs) +{ + char* filename; + char* database = NULL; + int err, flags=0; + static char* kwnames[] = { "filename", "dbname", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zi:remove", kwnames, + &filename, &database, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + EXTRACT_FROM_DOUBLE_LINKED_LIST_MAYBE_NULL(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->remove(self->db, filename, database, flags); + MYDB_END_ALLOW_THREADS; + + self->db = NULL; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + + +static PyObject* +DB_rename(DBObject* self, PyObject* args) +{ + char* filename; + char* database; + char* newname; + int err, flags=0; + + if (!PyArg_ParseTuple(args, "sss|i:rename", &filename, &database, &newname, + &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->rename(self->db, filename, database, newname, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_get_private(DBObject* self) +{ + /* We can give out the private field even if db is closed */ + Py_INCREF(self->private_obj); + return self->private_obj; +} + +static PyObject* +DB_set_private(DBObject* self, PyObject* private_obj) +{ + /* We can set the private field even if db is closed */ + Py_DECREF(self->private_obj); + Py_INCREF(private_obj); + self->private_obj = private_obj; + RETURN_NONE(); +} + + +static PyObject* +DB_set_bt_minkey(DBObject* self, PyObject* args) +{ + int err, minkey; + + if (!PyArg_ParseTuple(args,"i:set_bt_minkey", &minkey )) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_bt_minkey(self->db, minkey); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static int +_default_cmp(const DBT *leftKey, + const DBT *rightKey) +{ + int res; + int lsize = leftKey->size, rsize = rightKey->size; + + res = memcmp(leftKey->data, rightKey->data, + lsize < rsize ? lsize : rsize); + + if (res == 0) { + if (lsize < rsize) { + res = -1; + } + else if (lsize > rsize) { + res = 1; + } + } + return res; +} + +static int +_db_compareCallback(DB* db, + const DBT *leftKey, + const DBT *rightKey) +{ + int res = 0; + PyObject *args; + PyObject *result = NULL; + DBObject *self = (DBObject *)db->app_private; + + if (self == NULL || self->btCompareCallback == NULL) { + MYDB_BEGIN_BLOCK_THREADS; + PyErr_SetString(PyExc_TypeError, + (self == 0 + ? "DB_bt_compare db is NULL." + : "DB_bt_compare callback is NULL.")); + /* we're in a callback within the DB code, we can't raise */ + PyErr_Print(); + res = _default_cmp(leftKey, rightKey); + MYDB_END_BLOCK_THREADS; + } else { + MYDB_BEGIN_BLOCK_THREADS; + + args = BuildValue_SS(leftKey->data, leftKey->size, rightKey->data, rightKey->size); + if (args != NULL) { + /* XXX(twouters) I highly doubt this INCREF is correct */ + Py_INCREF(self); + result = PyEval_CallObject(self->btCompareCallback, args); + } + if (args == NULL || result == NULL) { + /* we're in a callback within the DB code, we can't raise */ + PyErr_Print(); + res = _default_cmp(leftKey, rightKey); + } else if (NUMBER_Check(result)) { + res = NUMBER_AsLong(result); + } else { + PyErr_SetString(PyExc_TypeError, + "DB_bt_compare callback MUST return an int."); + /* we're in a callback within the DB code, we can't raise */ + PyErr_Print(); + res = _default_cmp(leftKey, rightKey); + } + + Py_XDECREF(args); + Py_XDECREF(result); + + MYDB_END_BLOCK_THREADS; + } + return res; +} + +static PyObject* +DB_set_bt_compare(DBObject* self, PyObject* comparator) +{ + int err; + PyObject *tuple, *result; + + CHECK_DB_NOT_CLOSED(self); + + if (!PyCallable_Check(comparator)) { + makeTypeError("Callable", comparator); + return NULL; + } + + /* + * Perform a test call of the comparator function with two empty + * string objects here. verify that it returns an int (0). + * err if not. + */ + tuple = Py_BuildValue("(ss)", "", ""); + result = PyEval_CallObject(comparator, tuple); + Py_DECREF(tuple); + if (result == NULL) + return NULL; + if (!NUMBER_Check(result)) { + PyErr_SetString(PyExc_TypeError, + "callback MUST return an int"); + return NULL; + } else if (NUMBER_AsLong(result) != 0) { + PyErr_SetString(PyExc_TypeError, + "callback failed to return 0 on two empty strings"); + return NULL; + } + Py_DECREF(result); + + /* We don't accept multiple set_bt_compare operations, in order to + * simplify the code. This would have no real use, as one cannot + * change the function once the db is opened anyway */ + if (self->btCompareCallback != NULL) { + PyErr_SetString(PyExc_RuntimeError, "set_bt_compare() cannot be called more than once"); + return NULL; + } + + Py_INCREF(comparator); + self->btCompareCallback = comparator; + + /* This is to workaround a problem with un-initialized threads (see + comment in DB_associate) */ +#ifdef WITH_THREAD + PyEval_InitThreads(); +#endif + + err = self->db->set_bt_compare(self->db, _db_compareCallback); + + if (err) { + /* restore the old state in case of error */ + Py_DECREF(comparator); + self->btCompareCallback = NULL; + } + + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_cachesize(DBObject* self, PyObject* args) +{ + int err; + int gbytes = 0, bytes = 0, ncache = 0; + + if (!PyArg_ParseTuple(args,"ii|i:set_cachesize", + &gbytes,&bytes,&ncache)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_cachesize(self->db, gbytes, bytes, ncache); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_flags(DBObject* self, PyObject* args) +{ + int err, flags; + + if (!PyArg_ParseTuple(args,"i:set_flags", &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_flags(self->db, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + self->setflags |= flags; + RETURN_NONE(); +} + + +static PyObject* +DB_set_h_ffactor(DBObject* self, PyObject* args) +{ + int err, ffactor; + + if (!PyArg_ParseTuple(args,"i:set_h_ffactor", &ffactor)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_h_ffactor(self->db, ffactor); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_h_nelem(DBObject* self, PyObject* args) +{ + int err, nelem; + + if (!PyArg_ParseTuple(args,"i:set_h_nelem", &nelem)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_h_nelem(self->db, nelem); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_lorder(DBObject* self, PyObject* args) +{ + int err, lorder; + + if (!PyArg_ParseTuple(args,"i:set_lorder", &lorder)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_lorder(self->db, lorder); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_pagesize(DBObject* self, PyObject* args) +{ + int err, pagesize; + + if (!PyArg_ParseTuple(args,"i:set_pagesize", &pagesize)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_pagesize(self->db, pagesize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_re_delim(DBObject* self, PyObject* args) +{ + int err; + char delim; + + if (!PyArg_ParseTuple(args,"b:set_re_delim", &delim)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args,"c:set_re_delim", &delim)) + return NULL; + } + + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_delim(self->db, delim); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DB_set_re_len(DBObject* self, PyObject* args) +{ + int err, len; + + if (!PyArg_ParseTuple(args,"i:set_re_len", &len)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_len(self->db, len); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_re_pad(DBObject* self, PyObject* args) +{ + int err; + char pad; + + if (!PyArg_ParseTuple(args,"b:set_re_pad", &pad)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args,"c:set_re_pad", &pad)) + return NULL; + } + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_pad(self->db, pad); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_re_source(DBObject* self, PyObject* args) +{ + int err; + char *re_source; + + if (!PyArg_ParseTuple(args,"s:set_re_source", &re_source)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_source(self->db, re_source); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_q_extentsize(DBObject* self, PyObject* args) +{ + int err; + int extentsize; + + if (!PyArg_ParseTuple(args,"i:set_q_extentsize", &extentsize)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_q_extentsize(self->db, extentsize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DB_stat(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0, type; + void* sp; + PyObject* d; +#if (DBVER >= 43) + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + static char* kwnames[] = { "flags", "txn", NULL }; +#else + static char* kwnames[] = { "flags", NULL }; +#endif + +#if (DBVER >= 43) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iO:stat", kwnames, + &flags, &txnobj)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; +#else + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:stat", kwnames, &flags)) + return NULL; +#endif + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 43) + err = self->db->stat(self->db, txn, &sp, flags); +#else + err = self->db->stat(self->db, &sp, flags); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + self->haveStat = 1; + + /* Turn the stat structure into a dictionary */ + type = _DB_get_type(self); + if ((type == -1) || ((d = PyDict_New()) == NULL)) { + free(sp); + return NULL; + } + +#define MAKE_HASH_ENTRY(name) _addIntToDict(d, #name, ((DB_HASH_STAT*)sp)->hash_##name) +#define MAKE_BT_ENTRY(name) _addIntToDict(d, #name, ((DB_BTREE_STAT*)sp)->bt_##name) +#define MAKE_QUEUE_ENTRY(name) _addIntToDict(d, #name, ((DB_QUEUE_STAT*)sp)->qs_##name) + + switch (type) { + case DB_HASH: + MAKE_HASH_ENTRY(magic); + MAKE_HASH_ENTRY(version); + MAKE_HASH_ENTRY(nkeys); + MAKE_HASH_ENTRY(ndata); +#if (DBVER >= 46) + MAKE_HASH_ENTRY(pagecnt); +#endif + MAKE_HASH_ENTRY(pagesize); +#if (DBVER < 41) + MAKE_HASH_ENTRY(nelem); +#endif + MAKE_HASH_ENTRY(ffactor); + MAKE_HASH_ENTRY(buckets); + MAKE_HASH_ENTRY(free); + MAKE_HASH_ENTRY(bfree); + MAKE_HASH_ENTRY(bigpages); + MAKE_HASH_ENTRY(big_bfree); + MAKE_HASH_ENTRY(overflows); + MAKE_HASH_ENTRY(ovfl_free); + MAKE_HASH_ENTRY(dup); + MAKE_HASH_ENTRY(dup_free); + break; + + case DB_BTREE: + case DB_RECNO: + MAKE_BT_ENTRY(magic); + MAKE_BT_ENTRY(version); + MAKE_BT_ENTRY(nkeys); + MAKE_BT_ENTRY(ndata); +#if (DBVER >= 46) + MAKE_BT_ENTRY(pagecnt); +#endif + MAKE_BT_ENTRY(pagesize); + MAKE_BT_ENTRY(minkey); + MAKE_BT_ENTRY(re_len); + MAKE_BT_ENTRY(re_pad); + MAKE_BT_ENTRY(levels); + MAKE_BT_ENTRY(int_pg); + MAKE_BT_ENTRY(leaf_pg); + MAKE_BT_ENTRY(dup_pg); + MAKE_BT_ENTRY(over_pg); +#if (DBVER >= 43) + MAKE_BT_ENTRY(empty_pg); +#endif + MAKE_BT_ENTRY(free); + MAKE_BT_ENTRY(int_pgfree); + MAKE_BT_ENTRY(leaf_pgfree); + MAKE_BT_ENTRY(dup_pgfree); + MAKE_BT_ENTRY(over_pgfree); + break; + + case DB_QUEUE: + MAKE_QUEUE_ENTRY(magic); + MAKE_QUEUE_ENTRY(version); + MAKE_QUEUE_ENTRY(nkeys); + MAKE_QUEUE_ENTRY(ndata); + MAKE_QUEUE_ENTRY(pagesize); +#if (DBVER >= 41) + MAKE_QUEUE_ENTRY(extentsize); +#endif + MAKE_QUEUE_ENTRY(pages); + MAKE_QUEUE_ENTRY(re_len); + MAKE_QUEUE_ENTRY(re_pad); + MAKE_QUEUE_ENTRY(pgfree); +#if (DBVER == 31) + MAKE_QUEUE_ENTRY(start); +#endif + MAKE_QUEUE_ENTRY(first_recno); + MAKE_QUEUE_ENTRY(cur_recno); + break; + + default: + PyErr_SetString(PyExc_TypeError, "Unknown DB type, unable to stat"); + Py_DECREF(d); + d = NULL; + } + +#undef MAKE_HASH_ENTRY +#undef MAKE_BT_ENTRY +#undef MAKE_QUEUE_ENTRY + + free(sp); + return d; +} + +static PyObject* +DB_sync(DBObject* self, PyObject* args) +{ + int err; + int flags = 0; + + if (!PyArg_ParseTuple(args,"|i:sync", &flags )) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->sync(self->db, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_truncate(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + u_int32_t count=0; + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + static char* kwnames[] = { "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:cursor", kwnames, + &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->truncate(self->db, txn, &count, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(count); +} + + +static PyObject* +DB_upgrade(DBObject* self, PyObject* args) +{ + int err, flags=0; + char *filename; + + if (!PyArg_ParseTuple(args,"s|i:upgrade", &filename, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->upgrade(self->db, filename, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_verify(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + char* fileName; + char* dbName=NULL; + char* outFileName=NULL; + FILE* outFile=NULL; + static char* kwnames[] = { "filename", "dbname", "outfile", "flags", + NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zzi:verify", kwnames, + &fileName, &dbName, &outFileName, &flags)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (outFileName) + outFile = fopen(outFileName, "w"); + /* XXX(nnorwitz): it should probably be an exception if outFile + can't be opened. */ + + { /* DB.verify acts as a DB handle destructor (like close) */ + PyObject *error; + + error=DB_close_internal(self, 0, 1); + if (error ) { + return error; + } + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->verify(self->db, fileName, dbName, outFile, flags); + MYDB_END_ALLOW_THREADS; + + self->db = NULL; /* Implicit close; related objects already released */ + + if (outFile) + fclose(outFile); + + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_get_returns_none(DBObject* self, PyObject* args) +{ + int flags=0; + int oldValue=0; + + if (!PyArg_ParseTuple(args,"i:set_get_returns_none", &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + if (self->moduleFlags.getReturnsNone) + ++oldValue; + if (self->moduleFlags.cursorSetReturnsNone) + ++oldValue; + self->moduleFlags.getReturnsNone = (flags >= 1); + self->moduleFlags.cursorSetReturnsNone = (flags >= 2); + return NUMBER_FromLong(oldValue); +} + +#if (DBVER >= 41) +static PyObject* +DB_set_encrypt(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *passwd = NULL; + static char* kwnames[] = { "passwd", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:set_encrypt", kwnames, + &passwd, &flags)) { + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_encrypt(self->db, passwd, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif /* DBVER >= 41 */ + + +/*-------------------------------------------------------------- */ +/* Mapping and Dictionary-like access routines */ + +Py_ssize_t DB_length(PyObject* _self) +{ + int err; + Py_ssize_t size = 0; + int flags = 0; + void* sp; + DBObject* self = (DBObject*)_self; + + if (self->db == NULL) { + PyObject *t = Py_BuildValue("(is)", 0, "DB object has been closed"); + if (t) { + PyErr_SetObject(DBError, t); + Py_DECREF(t); + } + return -1; + } + + if (self->haveStat) { /* Has the stat function been called recently? If + so, we can use the cached value. */ + flags = DB_FAST_STAT; + } + + MYDB_BEGIN_ALLOW_THREADS; +redo_stat_for_length: +#if (DBVER >= 43) + err = self->db->stat(self->db, /*txnid*/ NULL, &sp, flags); +#else + err = self->db->stat(self->db, &sp, flags); +#endif + + /* All the stat structures have matching fields upto the ndata field, + so we can use any of them for the type cast */ + size = ((DB_BTREE_STAT*)sp)->bt_ndata; + + /* A size of 0 could mean that Berkeley DB no longer had the stat values cached. + * redo a full stat to make sure. + * Fixes SF python bug 1493322, pybsddb bug 1184012 + */ + if (size == 0 && (flags & DB_FAST_STAT)) { + flags = 0; + if (!err) + free(sp); + goto redo_stat_for_length; + } + + MYDB_END_ALLOW_THREADS; + + if (err) + return -1; + + self->haveStat = 1; + + free(sp); + return size; +} + + +PyObject* DB_subscript(DBObject* self, PyObject* keyobj) +{ + int err; + PyObject* retval; + DBT key; + DBT data; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell Berkeley DB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + } + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, NULL, &key, &data, 0); + MYDB_END_ALLOW_THREADS; + if (err == DB_NOTFOUND || err == DB_KEYEMPTY) { + PyErr_SetObject(PyExc_KeyError, keyobj); + retval = NULL; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + retval = Build_PyString(data.data, data.size); + FREE_DBT(data); + } + + FREE_DBT(key); + return retval; +} + + +static int +DB_ass_sub(DBObject* self, PyObject* keyobj, PyObject* dataobj) +{ + DBT key, data; + int retval; + int flags = 0; + + if (self->db == NULL) { + PyObject *t = Py_BuildValue("(is)", 0, "DB object has been closed"); + if (t) { + PyErr_SetObject(DBError, t); + Py_DECREF(t); + } + return -1; + } + + if (!make_key_dbt(self, keyobj, &key, NULL)) + return -1; + + if (dataobj != NULL) { + if (!make_dbt(dataobj, &data)) + retval = -1; + else { + if (self->setflags & (DB_DUP|DB_DUPSORT)) + /* dictionaries shouldn't have duplicate keys */ + flags = DB_NOOVERWRITE; + retval = _DB_put(self, NULL, &key, &data, flags); + + if ((retval == -1) && (self->setflags & (DB_DUP|DB_DUPSORT))) { + /* try deleting any old record that matches and then PUT it + * again... */ + _DB_delete(self, NULL, &key, 0); + PyErr_Clear(); + retval = _DB_put(self, NULL, &key, &data, flags); + } + } + } + else { + /* dataobj == NULL, so delete the key */ + retval = _DB_delete(self, NULL, &key, 0); + } + FREE_DBT(key); + return retval; +} + + +static PyObject* +DB_has_key(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + PyObject* keyobj; + DBT key, data; + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + static char* kwnames[] = {"key","txn", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:has_key", kwnames, + &keyobj, &txnobj)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) { + FREE_DBT(key); + return NULL; + } + + /* This causes DB_BUFFER_SMALL 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 + having to deal with data we won't be using. + */ + CLEAR_DBT(data); + data.flags = DB_DBT_USERMEM; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, 0); + MYDB_END_ALLOW_THREADS; + FREE_DBT(key); + + if (err == DB_BUFFER_SMALL || err == 0) { + return NUMBER_FromLong(1); + } else if (err == DB_NOTFOUND || err == DB_KEYEMPTY) { + return NUMBER_FromLong(0); + } + + makeDBError(err); + return NULL; +} + + +#define _KEYS_LIST 1 +#define _VALUES_LIST 2 +#define _ITEMS_LIST 3 + +static PyObject* +_DB_make_list(DBObject* self, DB_TXN* txn, int type) +{ + int err, dbtype; + DBT key; + DBT data; + DBC *cursor; + PyObject* list; + PyObject* item = NULL; + + CHECK_DB_NOT_CLOSED(self); + CLEAR_DBT(key); + CLEAR_DBT(data); + + dbtype = _DB_get_type(self); + if (dbtype == -1) + return NULL; + + list = PyList_New(0); + if (list == NULL) + return NULL; + + /* get a cursor */ + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->cursor(self->db, txn, &cursor, 0); + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + Py_DECREF(list); + return NULL; + } + + while (1) { /* use the cursor to traverse the DB, collecting items */ + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(cursor, &key, &data, DB_NEXT); + MYDB_END_ALLOW_THREADS; + + if (err) { + /* for any error, break out of the loop */ + break; + } + + switch (type) { + case _KEYS_LIST: + switch(dbtype) { + case DB_BTREE: + case DB_HASH: + default: + item = Build_PyString(key.data, key.size); + break; + case DB_RECNO: + case DB_QUEUE: + item = NUMBER_FromLong(*((db_recno_t*)key.data)); + break; + } + break; + + case _VALUES_LIST: + item = Build_PyString(data.data, data.size); + break; + + case _ITEMS_LIST: + switch(dbtype) { + case DB_BTREE: + case DB_HASH: + default: + item = BuildValue_SS(key.data, key.size, data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + item = BuildValue_IS(*((db_recno_t*)key.data), data.data, data.size); + break; + } + break; + default: + PyErr_Format(PyExc_ValueError, "Unknown key type 0x%x", type); + item = NULL; + break; + } + if (item == NULL) { + Py_DECREF(list); + list = NULL; + goto done; + } + if (PyList_Append(list, item)) { + Py_DECREF(list); + Py_DECREF(item); + list = NULL; + goto done; + } + Py_DECREF(item); + } + + /* DB_NOTFOUND || DB_KEYEMPTY is okay, it means we got to the end */ + if (err != DB_NOTFOUND && err != DB_KEYEMPTY && makeDBError(err)) { + Py_DECREF(list); + list = NULL; + } + + done: + MYDB_BEGIN_ALLOW_THREADS; + _DBC_close(cursor); + MYDB_END_ALLOW_THREADS; + return list; +} + + +static PyObject* +DB_keys(DBObject* self, PyObject* args) +{ + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + + if (!PyArg_UnpackTuple(args, "keys", 0, 1, &txnobj)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + return _DB_make_list(self, txn, _KEYS_LIST); +} + + +static PyObject* +DB_items(DBObject* self, PyObject* args) +{ + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + + if (!PyArg_UnpackTuple(args, "items", 0, 1, &txnobj)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + return _DB_make_list(self, txn, _ITEMS_LIST); +} + + +static PyObject* +DB_values(DBObject* self, PyObject* args) +{ + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + + if (!PyArg_UnpackTuple(args, "values", 0, 1, &txnobj)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + return _DB_make_list(self, txn, _VALUES_LIST); +} + +/* --------------------------------------------------------------------- */ +/* DBCursor methods */ + + +static PyObject* +DBC_close_internal(DBCursorObject* self) +{ + int err = 0; + + if (self->dbc != NULL) { + EXTRACT_FROM_DOUBLE_LINKED_LIST(self); + if (self->txn) { + EXTRACT_FROM_DOUBLE_LINKED_LIST_TXN(self); + self->txn=NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_close(self->dbc); + MYDB_END_ALLOW_THREADS; + self->dbc = NULL; + } + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBC_close(DBCursorObject* self) +{ + return DBC_close_internal(self); +} + + +static PyObject* +DBC_count(DBCursorObject* self, PyObject* args) +{ + int err = 0; + db_recno_t count; + int flags = 0; + + if (!PyArg_ParseTuple(args, "|i:count", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_count(self->dbc, &count, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + return NUMBER_FromLong(count); +} + + +static PyObject* +DBC_current(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_CURRENT,args,kwargs,"|iii:current"); +} + + +static PyObject* +DBC_delete(DBCursorObject* self, PyObject* args) +{ + int err, flags=0; + + if (!PyArg_ParseTuple(args, "|i:delete", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_del(self->dbc, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + self->mydb->haveStat = 0; + RETURN_NONE(); +} + + +static PyObject* +DBC_dup(DBCursorObject* self, PyObject* args) +{ + int err, flags =0; + DBC* dbc = NULL; + + if (!PyArg_ParseTuple(args, "|i:dup", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_dup(self->dbc, &dbc, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + return (PyObject*) newDBCursorObject(dbc, self->txn, self->mydb); +} + +static PyObject* +DBC_first(DBCursorObject* self, PyObject* args, PyObject* kwargs) +{ + return _DBCursor_get(self,DB_FIRST,args,kwargs,"|iii:first"); +} + + +static PyObject* +DBC_get(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + int err, flags=0; + PyObject* keyobj = NULL; + PyObject* dataobj = NULL; + PyObject* retval = NULL; + int dlen = -1; + int doff = -1; + DBT key, data; + static char* kwnames[] = { "key","data", "flags", "dlen", "doff", + NULL }; + + CLEAR_DBT(key); + CLEAR_DBT(data); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|ii:get", &kwnames[2], + &flags, &dlen, &doff)) + { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii:get", + &kwnames[1], + &keyobj, &flags, &dlen, &doff)) + { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOi|ii:get", + kwnames, &keyobj, &dataobj, + &flags, &dlen, &doff)) + { + return NULL; + } + } + } + + CHECK_CURSOR_NOT_CLOSED(self); + + if (keyobj && !make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + if ( (dataobj && !make_dbt(dataobj, &data)) || + (!add_partial_dbt(&data, dlen, doff)) ) + { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + 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 { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = BuildValue_SS(key.data, key.size, data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = BuildValue_IS(*((db_recno_t*)key.data), data.data, data.size); + break; + } + } + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + return retval; +} + +static PyObject* +DBC_pget(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + int err, flags=0; + PyObject* keyobj = NULL; + PyObject* dataobj = NULL; + PyObject* retval = NULL; + int dlen = -1; + int doff = -1; + DBT key, pkey, data; + static char* kwnames_keyOnly[] = { "key", "flags", "dlen", "doff", NULL }; + static char* kwnames[] = { "key", "data", "flags", "dlen", "doff", NULL }; + + CLEAR_DBT(key); + CLEAR_DBT(data); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|ii:pget", &kwnames[2], + &flags, &dlen, &doff)) + { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii:pget", + kwnames_keyOnly, + &keyobj, &flags, &dlen, &doff)) + { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOi|ii:pget", + kwnames, &keyobj, &dataobj, + &flags, &dlen, &doff)) + { + return NULL; + } + } + } + + CHECK_CURSOR_NOT_CLOSED(self); + + if (keyobj && !make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + if ( (dataobj && !make_dbt(dataobj, &data)) || + (!add_partial_dbt(&data, dlen, doff)) ) { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + return NULL; + } + + CLEAR_DBT(pkey); + pkey.flags = DB_DBT_MALLOC; + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_pget(self->dbc, &key, &pkey, &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 { + PyObject *pkeyObj; + PyObject *dataObj; + dataObj = Build_PyString(data.data, data.size); + + if (self->mydb->primaryDBType == DB_RECNO || + self->mydb->primaryDBType == DB_QUEUE) + pkeyObj = NUMBER_FromLong(*(int *)pkey.data); + else + pkeyObj = Build_PyString(pkey.data, pkey.size); + + if (key.data && key.size) /* return key, pkey and data */ + { + PyObject *keyObj; + int type = _DB_get_type(self->mydb); + if (type == DB_RECNO || type == DB_QUEUE) + keyObj = NUMBER_FromLong(*(int *)key.data); + else + keyObj = Build_PyString(key.data, key.size); +#if (PY_VERSION_HEX >= 0x02040000) + retval = PyTuple_Pack(3, keyObj, pkeyObj, dataObj); +#else + retval = Py_BuildValue("OOO", keyObj, pkeyObj, dataObj); +#endif + Py_DECREF(keyObj); + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + } + else /* return just the pkey and data */ + { +#if (PY_VERSION_HEX >= 0x02040000) + retval = PyTuple_Pack(2, pkeyObj, dataObj); +#else + retval = Py_BuildValue("OO", pkeyObj, dataObj); +#endif + } + Py_DECREF(dataObj); + Py_DECREF(pkeyObj); + FREE_DBT(pkey); + } + /* the only time REALLOC should be set is if we used an integer + * key that make_key_dbt malloc'd for us. always free these. */ + if (key.flags & DB_DBT_REALLOC) { /* 'make_key_dbt' could do a 'malloc' */ + FREE_DBT(key); + } + return retval; +} + + +static PyObject* +DBC_get_recno(DBCursorObject* self) +{ + int err; + db_recno_t recno; + DBT key; + DBT data; + + CHECK_CURSOR_NOT_CLOSED(self); + + CLEAR_DBT(key); + CLEAR_DBT(data); + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(self->dbc, &key, &data, DB_GET_RECNO); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + recno = *((db_recno_t*)data.data); + return NUMBER_FromLong(recno); +} + + +static PyObject* +DBC_last(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_LAST,args,kwargs,"|iii:last"); +} + + +static PyObject* +DBC_next(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_NEXT,args,kwargs,"|iii:next"); +} + + +static PyObject* +DBC_prev(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_PREV,args,kwargs,"|iii:prev"); +} + + +static PyObject* +DBC_put(DBCursorObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0; + PyObject* keyobj, *dataobj; + DBT key, data; + static char* kwnames[] = { "key", "data", "flags", "dlen", "doff", + NULL }; + int dlen = -1; + int doff = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iii:put", kwnames, + &keyobj, &dataobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + if (!make_dbt(dataobj, &data) || + !add_partial_dbt(&data, dlen, doff) ) + { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_put(self->dbc, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + RETURN_IF_ERR(); + self->mydb->haveStat = 0; + RETURN_NONE(); +} + + +static PyObject* +DBC_set(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + int err, flags = 0; + DBT key, data; + PyObject* retval, *keyobj; + static char* kwnames[] = { "key", "flags", "dlen", "doff", NULL }; + int dlen = -1; + int doff = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iii:set", kwnames, + &keyobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + + CLEAR_DBT(data); + if (!add_partial_dbt(&data, dlen, doff)) { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(self->dbc, &key, &data, flags|DB_SET); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) + && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = BuildValue_SS(key.data, key.size, data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = BuildValue_IS(*((db_recno_t*)key.data), data.data, data.size); + break; + } + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + } + /* the only time REALLOC should be set is if we used an integer + * key that make_key_dbt malloc'd for us. always free these. */ + if (key.flags & DB_DBT_REALLOC) { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + } + + return retval; +} + + +static PyObject* +DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0; + DBT key, data; + PyObject* retval, *keyobj; + static char* kwnames[] = { "key", "flags", "dlen", "doff", NULL }; + int dlen = -1; + int doff = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iii:set_range", kwnames, + &keyobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + + CLEAR_DBT(data); + if (!add_partial_dbt(&data, dlen, doff)) { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + return NULL; + } + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(self->dbc, &key, &data, flags|DB_SET_RANGE); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) + && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = BuildValue_SS(key.data, key.size, data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = BuildValue_IS(*((db_recno_t*)key.data), data.data, data.size); + break; + } + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + } + /* the only time REALLOC should be set is if we used an integer + * key that make_key_dbt malloc'd for us. always free these. */ + if (key.flags & DB_DBT_REALLOC) { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + } + + return retval; +} + +static PyObject* +_DBC_get_set_both(DBCursorObject* self, PyObject* keyobj, PyObject* dataobj, + int flags, unsigned int returnsNone) +{ + int err; + DBT key, data; + 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)) { + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(self->dbc, &key, &data, flags|DB_GET_BOTH); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) && returnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = BuildValue_SS(key.data, key.size, data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = BuildValue_IS(*((db_recno_t*)key.data), data.data, data.size); + break; + } + } + + FREE_DBT(key); /* 'make_key_dbt' could do a 'malloc' */ + 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); +} + +/* Return size of entry */ +static PyObject* +DBC_get_current_size(DBCursorObject* self) +{ + int err, flags=DB_CURRENT; + PyObject* retval = NULL; + DBT key, data; + + CHECK_CURSOR_NOT_CLOSED(self); + CLEAR_DBT(key); + CLEAR_DBT(data); + + /* We don't allocate any memory, forcing a DB_BUFFER_SMALL error and thus + getting the record size. */ + data.flags = DB_DBT_USERMEM; + data.ulen = 0; + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(self->dbc, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + if (err == DB_BUFFER_SMALL || !err) { + /* DB_BUFFER_SMALL means positive size, !err means zero length value */ + retval = NUMBER_FromLong((long)data.size); + err = 0; + } + + RETURN_IF_ERR(); + return retval; +} + +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) +{ + int err, irecno, flags=0; + db_recno_t recno; + DBT key, data; + PyObject* retval; + int dlen = -1; + int doff = -1; + static char* kwnames[] = { "recno","flags", "dlen", "doff", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|iii:set_recno", kwnames, + &irecno, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + CLEAR_DBT(key); + recno = (db_recno_t) irecno; + /* use allocated space so DB will be able to realloc room for the real + * key */ + key.data = malloc(sizeof(db_recno_t)); + if (key.data == NULL) { + PyErr_SetString(PyExc_MemoryError, "Key memory allocation failed"); + return NULL; + } + key.size = sizeof(db_recno_t); + key.ulen = key.size; + memcpy(key.data, &recno, sizeof(db_recno_t)); + key.flags = DB_DBT_REALLOC; + + CLEAR_DBT(data); + if (!add_partial_dbt(&data, dlen, doff)) { + FREE_DBT(key); + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(self->dbc, &key, &data, flags|DB_SET_RECNO); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND || err == DB_KEYEMPTY) + && 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 */ + retval = BuildValue_SS(key.data, key.size, data.data, data.size); + } + FREE_DBT(key); + + return retval; +} + + +static PyObject* +DBC_consume(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_CONSUME,args,kwargs,"|iii:consume"); +} + + +static PyObject* +DBC_next_dup(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_NEXT_DUP,args,kwargs,"|iii:next_dup"); +} + + +static PyObject* +DBC_next_nodup(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_NEXT_NODUP,args,kwargs,"|iii:next_nodup"); +} + + +static PyObject* +DBC_prev_nodup(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_PREV_NODUP,args,kwargs,"|iii:prev_nodup"); +} + + +static PyObject* +DBC_join_item(DBCursorObject* self, PyObject* args) +{ + int err, flags=0; + DBT key, data; + PyObject* retval; + + if (!PyArg_ParseTuple(args, "|i:join_item", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + CLEAR_DBT(key); + CLEAR_DBT(data); + + MYDB_BEGIN_ALLOW_THREADS; + err = _DBC_get(self->dbc, &key, &data, flags | DB_JOIN_ITEM); + 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 { + retval = BuildValue_S(key.data, key.size); + } + + return retval; +} + + + +/* --------------------------------------------------------------------- */ +/* DBEnv methods */ + + +static PyObject* +DBEnv_close_internal(DBEnvObject* self, int flags) +{ + PyObject *dummy; + int err; + + if (!self->closed) { /* Don't close more than once */ + while(self->children_txns) { + dummy=DBTxn_abort_discard_internal(self->children_txns,0); + Py_XDECREF(dummy); + } + while(self->children_dbs) { + dummy=DB_close_internal(self->children_dbs, 0, 0); + Py_XDECREF(dummy); + } + } + + self->closed = 1; + if (self->db_env) { + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->close(self->db_env, flags); + MYDB_END_ALLOW_THREADS; + /* after calling DBEnv->close, regardless of error, this DBEnv + * may not be accessed again (Berkeley DB docs). */ + self->db_env = NULL; + RETURN_IF_ERR(); + } + RETURN_NONE(); +} + +static PyObject* +DBEnv_close(DBEnvObject* self, PyObject* args) +{ + int flags = 0; + + if (!PyArg_ParseTuple(args, "|i:close", &flags)) + return NULL; + return DBEnv_close_internal(self, flags); +} + + +static PyObject* +DBEnv_open(DBEnvObject* self, PyObject* args) +{ + int err, flags=0, mode=0660; + char *db_home; + + if (!PyArg_ParseTuple(args, "z|ii:open", &db_home, &flags, &mode)) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->open(self->db_env, db_home, flags, mode); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + self->closed = 0; + self->flags = flags; + RETURN_NONE(); +} + + +static PyObject* +DBEnv_remove(DBEnvObject* self, PyObject* args) +{ + int err, flags=0; + char *db_home; + + if (!PyArg_ParseTuple(args, "s|i:remove", &db_home, &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->remove(self->db_env, db_home, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +#if (DBVER >= 41) +static PyObject* +DBEnv_dbremove(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *file = NULL; + char *database = NULL; + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + static char* kwnames[] = { "file", "database", "txn", "flags", + NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zOi:dbremove", kwnames, + &file, &database, &txnobj, &flags)) { + return NULL; + } + if (!checkTxnObj(txnobj, &txn)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->dbremove(self->db_env, txn, file, database, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_dbrename(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *file = NULL; + char *database = NULL; + char *newname = NULL; + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + static char* kwnames[] = { "file", "database", "newname", "txn", + "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "szs|Oi:dbrename", kwnames, + &file, &database, &newname, &txnobj, &flags)) { + return NULL; + } + if (!checkTxnObj(txnobj, &txn)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->dbrename(self->db_env, txn, file, database, newname, + flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_encrypt(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *passwd = NULL; + static char* kwnames[] = { "passwd", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:set_encrypt", kwnames, + &passwd, &flags)) { + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_encrypt(self->db_env, passwd, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif /* DBVER >= 41 */ + +static PyObject* +DBEnv_set_timeout(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + u_int32_t timeout = 0; + static char* kwnames[] = { "timeout", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii:set_timeout", kwnames, + &timeout, &flags)) { + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_timeout(self->db_env, (db_timeout_t)timeout, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_shm_key(DBEnvObject* self, PyObject* args) +{ + int err; + long shm_key = 0; + + if (!PyArg_ParseTuple(args, "l:set_shm_key", &shm_key)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + err = self->db_env->set_shm_key(self->db_env, shm_key); + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_cachesize(DBEnvObject* self, PyObject* args) +{ + int err, gbytes=0, bytes=0, ncache=0; + + if (!PyArg_ParseTuple(args, "ii|i:set_cachesize", + &gbytes, &bytes, &ncache)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_cachesize(self->db_env, gbytes, bytes, ncache); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_flags(DBEnvObject* self, PyObject* args) +{ + int err, flags=0, onoff=0; + + if (!PyArg_ParseTuple(args, "ii:set_flags", + &flags, &onoff)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_flags(self->db_env, flags, onoff); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +#if (DBVER >= 47) +static PyObject* +DBEnv_log_set_config(DBEnvObject* self, PyObject* args) +{ + int err, flags, onoff; + + if (!PyArg_ParseTuple(args, "ii:log_set_config", + &flags, &onoff)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->log_set_config(self->db_env, flags, onoff); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif /* DBVER >= 47 */ + + +static PyObject* +DBEnv_set_data_dir(DBEnvObject* self, PyObject* args) +{ + int err; + char *dir; + + if (!PyArg_ParseTuple(args, "s:set_data_dir", &dir)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_data_dir(self->db_env, dir); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lg_bsize(DBEnvObject* self, PyObject* args) +{ + int err, lg_bsize; + + if (!PyArg_ParseTuple(args, "i:set_lg_bsize", &lg_bsize)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lg_bsize(self->db_env, lg_bsize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lg_dir(DBEnvObject* self, PyObject* args) +{ + int err; + char *dir; + + if (!PyArg_ParseTuple(args, "s:set_lg_dir", &dir)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lg_dir(self->db_env, dir); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_lg_max(DBEnvObject* self, PyObject* args) +{ + int err, lg_max; + + if (!PyArg_ParseTuple(args, "i:set_lg_max", &lg_max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lg_max(self->db_env, lg_max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +#if (DBVER >= 42) +static PyObject* +DBEnv_get_lg_max(DBEnvObject* self) +{ + int err; + u_int32_t lg_max; + + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->get_lg_max(self->db_env, &lg_max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(lg_max); +} +#endif + + +static PyObject* +DBEnv_set_lg_regionmax(DBEnvObject* self, PyObject* args) +{ + int err, lg_max; + + if (!PyArg_ParseTuple(args, "i:set_lg_regionmax", &lg_max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lg_regionmax(self->db_env, lg_max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lk_detect(DBEnvObject* self, PyObject* args) +{ + int err, lk_detect; + + if (!PyArg_ParseTuple(args, "i:set_lk_detect", &lk_detect)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_detect(self->db_env, lk_detect); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +#if (DBVER < 45) +static PyObject* +DBEnv_set_lk_max(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif + + + +static PyObject* +DBEnv_set_lk_max_locks(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max_locks", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max_locks(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lk_max_lockers(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max_lockers", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max_lockers(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lk_max_objects(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max_objects", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max_objects(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_mp_mmapsize(DBEnvObject* self, PyObject* args) +{ + int err, mp_mmapsize; + + if (!PyArg_ParseTuple(args, "i:set_mp_mmapsize", &mp_mmapsize)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_mp_mmapsize(self->db_env, mp_mmapsize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_tmp_dir(DBEnvObject* self, PyObject* args) +{ + int err; + char *dir; + + if (!PyArg_ParseTuple(args, "s:set_tmp_dir", &dir)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_tmp_dir(self->db_env, dir); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_txn_recover(DBEnvObject* self) +{ + int flags = DB_FIRST; + int err, i; + PyObject *list, *tuple, *gid; + DBTxnObject *txn; +#define PREPLIST_LEN 16 + DB_PREPLIST preplist[PREPLIST_LEN]; + long retp; + + CHECK_ENV_NOT_CLOSED(self); + + list=PyList_New(0); + if (!list) + return NULL; + while (!0) { + MYDB_BEGIN_ALLOW_THREADS + err=self->db_env->txn_recover(self->db_env, + preplist, PREPLIST_LEN, &retp, flags); +#undef PREPLIST_LEN + MYDB_END_ALLOW_THREADS + if (err) { + Py_DECREF(list); + RETURN_IF_ERR(); + } + if (!retp) break; + flags=DB_NEXT; /* Prepare for next loop pass */ + for (i=0; iflag_prepare=1; /* Recover state */ + tuple=PyTuple_New(2); + if (!tuple) { + Py_DECREF(list); + Py_DECREF(gid); + Py_DECREF(txn); + return NULL; + } + if (PyTuple_SetItem(tuple, 0, gid)) { + Py_DECREF(list); + Py_DECREF(gid); + Py_DECREF(txn); + Py_DECREF(tuple); + return NULL; + } + if (PyTuple_SetItem(tuple, 1, (PyObject *)txn)) { + Py_DECREF(list); + Py_DECREF(txn); + Py_DECREF(tuple); /* This delete the "gid" also */ + return NULL; + } + if (PyList_Append(list, tuple)) { + Py_DECREF(list); + Py_DECREF(tuple);/* This delete the "gid" and the "txn" also */ + return NULL; + } + Py_DECREF(tuple); + } + } + return list; +} + +static PyObject* +DBEnv_txn_begin(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int flags = 0; + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + static char* kwnames[] = { "parent", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:txn_begin", kwnames, + &txnobj, &flags)) + return NULL; + + if (!checkTxnObj(txnobj, &txn)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + return (PyObject*)newDBTxnObject(self, (DBTxnObject *)txnobj, NULL, flags); +} + + +static PyObject* +DBEnv_txn_checkpoint(DBEnvObject* self, PyObject* args) +{ + int err, kbyte=0, min=0, flags=0; + + if (!PyArg_ParseTuple(args, "|iii:txn_checkpoint", &kbyte, &min, &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->txn_checkpoint(self->db_env, kbyte, min, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_tx_max(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_tx_max", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + err = self->db_env->set_tx_max(self->db_env, max); + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_tx_timestamp(DBEnvObject* self, PyObject* args) +{ + int err; + long stamp; + time_t timestamp; + + if (!PyArg_ParseTuple(args, "l:set_tx_timestamp", &stamp)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + timestamp = (time_t)stamp; + err = self->db_env->set_tx_timestamp(self->db_env, ×tamp); + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_lock_detect(DBEnvObject* self, PyObject* args) +{ + int err, atype, flags=0; + int aborted = 0; + + if (!PyArg_ParseTuple(args, "i|i:lock_detect", &atype, &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->lock_detect(self->db_env, flags, atype, &aborted); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(aborted); +} + + +static PyObject* +DBEnv_lock_get(DBEnvObject* self, PyObject* args) +{ + int flags=0; + int locker, lock_mode; + DBT obj; + PyObject* objobj; + + if (!PyArg_ParseTuple(args, "iOi|i:lock_get", &locker, &objobj, &lock_mode, &flags)) + return NULL; + + + if (!make_dbt(objobj, &obj)) + return NULL; + + return (PyObject*)newDBLockObject(self, locker, &obj, lock_mode, flags); +} + + +static PyObject* +DBEnv_lock_id(DBEnvObject* self) +{ + int err; + u_int32_t theID; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->lock_id(self->db_env, &theID); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + return NUMBER_FromLong((long)theID); +} + +static PyObject* +DBEnv_lock_id_free(DBEnvObject* self, PyObject* args) +{ + int err; + u_int32_t theID; + + if (!PyArg_ParseTuple(args, "I:lock_id_free", &theID)) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->lock_id_free(self->db_env, theID); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_lock_put(DBEnvObject* self, PyObject* args) +{ + int err; + DBLockObject* dblockobj; + + if (!PyArg_ParseTuple(args, "O!:lock_put", &DBLock_Type, &dblockobj)) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->lock_put(self->db_env, &dblockobj->lock); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +#if (DBVER >= 44) +static PyObject* +DBEnv_lsn_reset(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + char *file; + u_int32_t flags = 0; + static char* kwnames[] = { "file", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|i:lsn_reset", kwnames, + &file, &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->lsn_reset(self->db_env, file, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif /* DBVER >= 4.4 */ + +static PyObject* +DBEnv_log_stat(DBEnvObject* self, PyObject* args) +{ + int err; + DB_LOG_STAT* statp = NULL; + PyObject* d = NULL; + u_int32_t flags = 0; + + if (!PyArg_ParseTuple(args, "|i:log_stat", &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->log_stat(self->db_env, &statp, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + /* Turn the stat structure into a dictionary */ + d = PyDict_New(); + if (d == NULL) { + if (statp) + free(statp); + return NULL; + } + +#define MAKE_ENTRY(name) _addIntToDict(d, #name, statp->st_##name) + + MAKE_ENTRY(magic); + MAKE_ENTRY(version); + MAKE_ENTRY(mode); + MAKE_ENTRY(lg_bsize); +#if (DBVER >= 44) + MAKE_ENTRY(lg_size); + MAKE_ENTRY(record); +#endif +#if (DBVER < 41) + MAKE_ENTRY(lg_max); +#endif + MAKE_ENTRY(w_mbytes); + MAKE_ENTRY(w_bytes); + MAKE_ENTRY(wc_mbytes); + MAKE_ENTRY(wc_bytes); + MAKE_ENTRY(wcount); + MAKE_ENTRY(wcount_fill); +#if (DBVER >= 44) + MAKE_ENTRY(rcount); +#endif + MAKE_ENTRY(scount); + MAKE_ENTRY(cur_file); + MAKE_ENTRY(cur_offset); + MAKE_ENTRY(disk_file); + MAKE_ENTRY(disk_offset); + MAKE_ENTRY(maxcommitperflush); + MAKE_ENTRY(mincommitperflush); + MAKE_ENTRY(regsize); + MAKE_ENTRY(region_wait); + MAKE_ENTRY(region_nowait); + +#undef MAKE_ENTRY + free(statp); + return d; +} /* DBEnv_log_stat */ + + +static PyObject* +DBEnv_lock_stat(DBEnvObject* self, PyObject* args) +{ + int err; + DB_LOCK_STAT* sp; + PyObject* d = NULL; + u_int32_t flags = 0; + + if (!PyArg_ParseTuple(args, "|i:lock_stat", &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->lock_stat(self->db_env, &sp, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + /* Turn the stat structure into a dictionary */ + d = PyDict_New(); + if (d == NULL) { + free(sp); + return NULL; + } + +#define MAKE_ENTRY(name) _addIntToDict(d, #name, sp->st_##name) + +#if (DBVER < 41) + MAKE_ENTRY(lastid); +#endif +#if (DBVER >=41) + MAKE_ENTRY(id); + MAKE_ENTRY(cur_maxid); +#endif + MAKE_ENTRY(nmodes); + MAKE_ENTRY(maxlocks); + MAKE_ENTRY(maxlockers); + MAKE_ENTRY(maxobjects); + MAKE_ENTRY(nlocks); + MAKE_ENTRY(maxnlocks); + MAKE_ENTRY(nlockers); + MAKE_ENTRY(maxnlockers); + MAKE_ENTRY(nobjects); + MAKE_ENTRY(maxnobjects); + MAKE_ENTRY(nrequests); + MAKE_ENTRY(nreleases); +#if (DBVER >= 44) + MAKE_ENTRY(nupgrade); + MAKE_ENTRY(ndowngrade); +#endif +#if (DBVER < 44) + MAKE_ENTRY(nnowaits); /* these were renamed in 4.4 */ + MAKE_ENTRY(nconflicts); +#else + MAKE_ENTRY(lock_nowait); + MAKE_ENTRY(lock_wait); +#endif + MAKE_ENTRY(ndeadlocks); +#if (DBVER >= 41) + MAKE_ENTRY(locktimeout); + MAKE_ENTRY(txntimeout); +#endif + MAKE_ENTRY(nlocktimeouts); + MAKE_ENTRY(ntxntimeouts); +#if (DBVER >= 46) + MAKE_ENTRY(objs_wait); + MAKE_ENTRY(objs_nowait); + MAKE_ENTRY(lockers_wait); + MAKE_ENTRY(lockers_nowait); +#if (DBVER >= 47) + MAKE_ENTRY(lock_wait); + MAKE_ENTRY(lock_nowait); +#else + MAKE_ENTRY(locks_wait); + MAKE_ENTRY(locks_nowait); +#endif + MAKE_ENTRY(hash_len); +#endif + MAKE_ENTRY(regsize); + MAKE_ENTRY(region_wait); + MAKE_ENTRY(region_nowait); + +#undef MAKE_ENTRY + free(sp); + return d; +} + +static PyObject* +DBEnv_log_flush(DBEnvObject* self) +{ + int err; + + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS + err = self->db_env->log_flush(self->db_env, NULL); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_log_archive(DBEnvObject* self, PyObject* args) +{ + int flags=0; + int err; + char **log_list = NULL; + PyObject* list; + PyObject* item = NULL; + + if (!PyArg_ParseTuple(args, "|i:log_archive", &flags)) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->log_archive(self->db_env, &log_list, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + list = PyList_New(0); + if (list == NULL) { + if (log_list) + free(log_list); + return NULL; + } + + if (log_list) { + char **log_list_start; + for (log_list_start = log_list; *log_list != NULL; ++log_list) { + item = PyBytes_FromString (*log_list); + if (item == NULL) { + Py_DECREF(list); + list = NULL; + break; + } + if (PyList_Append(list, item)) { + Py_DECREF(list); + list = NULL; + Py_DECREF(item); + break; + } + Py_DECREF(item); + } + free(log_list_start); + } + return list; +} + + +static PyObject* +DBEnv_txn_stat(DBEnvObject* self, PyObject* args) +{ + int err; + DB_TXN_STAT* sp; + PyObject* d = NULL; + u_int32_t flags=0; + + if (!PyArg_ParseTuple(args, "|i:txn_stat", &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->txn_stat(self->db_env, &sp, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + /* Turn the stat structure into a dictionary */ + d = PyDict_New(); + if (d == NULL) { + free(sp); + return NULL; + } + +#define MAKE_ENTRY(name) _addIntToDict(d, #name, sp->st_##name) +#define MAKE_TIME_T_ENTRY(name) _addTimeTToDict(d, #name, sp->st_##name) +#define MAKE_DB_LSN_ENTRY(name) _addDB_lsnToDict(d, #name, sp->st_##name) + + MAKE_DB_LSN_ENTRY(last_ckp); + MAKE_TIME_T_ENTRY(time_ckp); + MAKE_ENTRY(last_txnid); + MAKE_ENTRY(maxtxns); + MAKE_ENTRY(nactive); + MAKE_ENTRY(maxnactive); +#if (DBVER >= 45) + MAKE_ENTRY(nsnapshot); + MAKE_ENTRY(maxnsnapshot); +#endif + MAKE_ENTRY(nbegins); + MAKE_ENTRY(naborts); + MAKE_ENTRY(ncommits); + MAKE_ENTRY(nrestores); + MAKE_ENTRY(regsize); + MAKE_ENTRY(region_wait); + MAKE_ENTRY(region_nowait); + +#undef MAKE_DB_LSN_ENTRY +#undef MAKE_ENTRY +#undef MAKE_TIME_T_ENTRY + free(sp); + return d; +} + + +static PyObject* +DBEnv_set_get_returns_none(DBEnvObject* self, PyObject* args) +{ + int flags=0; + int oldValue=0; + + if (!PyArg_ParseTuple(args,"i:set_get_returns_none", &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + if (self->moduleFlags.getReturnsNone) + ++oldValue; + if (self->moduleFlags.cursorSetReturnsNone) + ++oldValue; + self->moduleFlags.getReturnsNone = (flags >= 1); + self->moduleFlags.cursorSetReturnsNone = (flags >= 2); + return NUMBER_FromLong(oldValue); +} + +static PyObject* +DBEnv_get_private(DBEnvObject* self) +{ + /* We can give out the private field even if dbenv is closed */ + Py_INCREF(self->private_obj); + return self->private_obj; +} + +static PyObject* +DBEnv_set_private(DBEnvObject* self, PyObject* private_obj) +{ + /* We can set the private field even if dbenv is closed */ + Py_DECREF(self->private_obj); + Py_INCREF(private_obj); + self->private_obj = private_obj; + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_rpc_server(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + char *host; + long cl_timeout=0, sv_timeout=0; + + static char* kwnames[] = { "host", "cl_timeout", "sv_timeout", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ll:set_rpc_server", kwnames, + &host, &cl_timeout, &sv_timeout)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_rpc_server(self->db_env, NULL, host, cl_timeout, + sv_timeout, 0); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_verbose(DBEnvObject* self, PyObject* args) +{ + int err; + int which, onoff; + + if (!PyArg_ParseTuple(args, "ii:set_verbose", &which, &onoff)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_verbose(self->db_env, which, onoff); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +#if (DBVER >= 42) +static PyObject* +DBEnv_get_verbose(DBEnvObject* self, PyObject* args) +{ + int err; + int which; + int verbose; + + if (!PyArg_ParseTuple(args, "i:get_verbose", &which)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->get_verbose(self->db_env, which, &verbose); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return PyBool_FromLong(verbose); +} +#endif + +#if (DBVER >= 45) +static void +_dbenv_event_notifyCallback(DB_ENV* db_env, u_int32_t event, void *event_info) +{ + DBEnvObject *dbenv; + PyObject* callback; + PyObject* args; + PyObject* result = NULL; + + MYDB_BEGIN_BLOCK_THREADS; + dbenv = (DBEnvObject *)db_env->app_private; + callback = dbenv->event_notifyCallback; + if (callback) { + if (event == DB_EVENT_REP_NEWMASTER) { + args = Py_BuildValue("(Oii)", dbenv, event, *((int *)event_info)); + } else { + args = Py_BuildValue("(OiO)", dbenv, event, Py_None); + } + if (args) { + result = PyEval_CallObject(callback, args); + } + if ((!args) || (!result)) { + PyErr_Print(); + } + Py_XDECREF(args); + Py_XDECREF(result); + } + MYDB_END_BLOCK_THREADS; +} +#endif + +#if (DBVER >= 45) +static PyObject* +DBEnv_set_event_notify(DBEnvObject* self, PyObject* notifyFunc) +{ + int err; + + CHECK_ENV_NOT_CLOSED(self); + + if (!PyCallable_Check(notifyFunc)) { + makeTypeError("Callable", notifyFunc); + return NULL; + } + + Py_XDECREF(self->event_notifyCallback); + Py_INCREF(notifyFunc); + self->event_notifyCallback = notifyFunc; + + /* This is to workaround a problem with un-initialized threads (see + comment in DB_associate) */ +#ifdef WITH_THREAD + PyEval_InitThreads(); +#endif + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_event_notify(self->db_env, _dbenv_event_notifyCallback); + MYDB_END_ALLOW_THREADS; + + if (err) { + Py_DECREF(notifyFunc); + self->event_notifyCallback = NULL; + } + + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif + + +/* --------------------------------------------------------------------- */ +/* REPLICATION METHODS: Base Replication */ + + +static PyObject* +DBEnv_rep_process_message(DBEnvObject* self, PyObject* args) +{ + int err; + PyObject *control_py, *rec_py; + DBT control, rec; + int envid; +#if (DBVER >= 42) + DB_LSN lsn; +#endif + + if (!PyArg_ParseTuple(args, "OOi:rep_process_message", &control_py, + &rec_py, &envid)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + if (!make_dbt(control_py, &control)) + return NULL; + if (!make_dbt(rec_py, &rec)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 46) + err = self->db_env->rep_process_message(self->db_env, &control, &rec, + envid, &lsn); +#else +#if (DBVER >= 42) + err = self->db_env->rep_process_message(self->db_env, &control, &rec, + &envid, &lsn); +#else + err = self->db_env->rep_process_message(self->db_env, &control, &rec, + &envid); +#endif +#endif + MYDB_END_ALLOW_THREADS; + switch (err) { + case DB_REP_NEWMASTER : + return Py_BuildValue("(iO)", envid, Py_None); + break; + + case DB_REP_DUPMASTER : + case DB_REP_HOLDELECTION : +#if (DBVER >= 44) + case DB_REP_IGNORE : + case DB_REP_JOIN_FAILURE : +#endif + return Py_BuildValue("(iO)", err, Py_None); + break; + case DB_REP_NEWSITE : + { + PyObject *tmp, *r; + + if (!(tmp = PyBytes_FromStringAndSize(rec.data, rec.size))) { + return NULL; + } + + r = Py_BuildValue("(iO)", err, tmp); + Py_DECREF(tmp); + return r; + break; + } +#if (DBVER >= 42) + case DB_REP_NOTPERM : + case DB_REP_ISPERM : + return Py_BuildValue("(i(ll))", err, lsn.file, lsn.offset); + break; +#endif + } + RETURN_IF_ERR(); + return Py_BuildValue("(OO)", Py_None, Py_None); +} + +static int +_DBEnv_rep_transportCallback(DB_ENV* db_env, const DBT* control, const DBT* rec, + const DB_LSN *lsn, int envid, u_int32_t flags) +{ + DBEnvObject *dbenv; + PyObject* rep_transport; + PyObject* args; + PyObject *a, *b; + PyObject* result = NULL; + int ret=0; + + MYDB_BEGIN_BLOCK_THREADS; + dbenv = (DBEnvObject *)db_env->app_private; + rep_transport = dbenv->rep_transport; + + /* + ** The errors in 'a' or 'b' are detected in "Py_BuildValue". + */ + a = PyBytes_FromStringAndSize(control->data, control->size); + b = PyBytes_FromStringAndSize(rec->data, rec->size); + + args = Py_BuildValue( +#if (PY_VERSION_HEX >= 0x02040000) + "(OOO(ll)iI)", +#else + "(OOO(ll)ii)", +#endif + dbenv, + a, b, + lsn->file, lsn->offset, envid, flags); + if (args) { + result = PyEval_CallObject(rep_transport, args); + } + + if ((!args) || (!result)) { + PyErr_Print(); + ret = -1; + } + Py_XDECREF(a); + Py_XDECREF(b); + Py_XDECREF(args); + Py_XDECREF(result); + MYDB_END_BLOCK_THREADS; + return ret; +} + +#if (DBVER <= 41) +static int +_DBEnv_rep_transportCallbackOLD(DB_ENV* db_env, const DBT* control, const DBT* rec, + int envid, u_int32_t flags) +{ + DB_LSN lsn; + + lsn.file = -1; /* Dummy values */ + lsn.offset = -1; + return _DBEnv_rep_transportCallback(db_env, control, rec, &lsn, envid, + flags); +} +#endif + +static PyObject* +DBEnv_rep_set_transport(DBEnvObject* self, PyObject* args) +{ + int err; + int envid; + PyObject *rep_transport; + + if (!PyArg_ParseTuple(args, "iO:rep_set_transport", &envid, &rep_transport)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + if (!PyCallable_Check(rep_transport)) { + makeTypeError("Callable", rep_transport); + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >=45) + err = self->db_env->rep_set_transport(self->db_env, envid, + &_DBEnv_rep_transportCallback); +#else +#if (DBVER >= 42) + err = self->db_env->set_rep_transport(self->db_env, envid, + &_DBEnv_rep_transportCallback); +#else + err = self->db_env->set_rep_transport(self->db_env, envid, + &_DBEnv_rep_transportCallbackOLD); +#endif +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + Py_DECREF(self->rep_transport); + Py_INCREF(rep_transport); + self->rep_transport = rep_transport; + RETURN_NONE(); +} + +#if (DBVER >= 47) +static PyObject* +DBEnv_rep_set_request(DBEnvObject* self, PyObject* args) +{ + int err; + unsigned int minimum, maximum; + + if (!PyArg_ParseTuple(args,"II:rep_set_request", &minimum, &maximum)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_set_request(self->db_env, minimum, maximum); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_rep_get_request(DBEnvObject* self) +{ + int err; + u_int32_t minimum, maximum; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_get_request(self->db_env, &minimum, &maximum); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); +#if (PY_VERSION_HEX >= 0x02040000) + return Py_BuildValue("II", minimum, maximum); +#else + return Py_BuildValue("ii", minimum, maximum); +#endif +} +#endif + +#if (DBVER >= 45) +static PyObject* +DBEnv_rep_set_limit(DBEnvObject* self, PyObject* args) +{ + int err; + int limit; + + if (!PyArg_ParseTuple(args,"i:rep_set_limit", &limit)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_set_limit(self->db_env, 0, limit); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_rep_get_limit(DBEnvObject* self) +{ + int err; + u_int32_t gbytes, bytes; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_get_limit(self->db_env, &gbytes, &bytes); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(bytes); +} +#endif + +#if (DBVER >= 44) +static PyObject* +DBEnv_rep_set_config(DBEnvObject* self, PyObject* args) +{ + int err; + int which; + int onoff; + + if (!PyArg_ParseTuple(args,"ii:rep_set_config", &which, &onoff)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_set_config(self->db_env, which, onoff); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_rep_get_config(DBEnvObject* self, PyObject* args) +{ + int err; + int which; + int onoff; + + if (!PyArg_ParseTuple(args, "i:rep_get_config", &which)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_get_config(self->db_env, which, &onoff); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return PyBool_FromLong(onoff); +} +#endif + +#if (DBVER >= 46) +static PyObject* +DBEnv_rep_elect(DBEnvObject* self, PyObject* args) +{ + int err; + u_int32_t nsites, nvotes; + + if (!PyArg_ParseTuple(args, "II:rep_elect", &nsites, &nvotes)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_elect(self->db_env, nvotes, nvotes, 0); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif + +static PyObject* +DBEnv_rep_start(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + PyObject *cdata_py = Py_None; + DBT cdata; + int flags; + static char* kwnames[] = {"flags","cdata", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "i|O:rep_start", kwnames, &flags, &cdata_py)) + { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + + if (!make_dbt(cdata_py, &cdata)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_start(self->db_env, cdata.size ? &cdata : NULL, + flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +#if (DBVER >= 44) +static PyObject* +DBEnv_rep_sync(DBEnvObject* self) +{ + int err; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_sync(self->db_env, 0); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif + + +#if (DBVER >= 45) +static PyObject* +DBEnv_rep_set_nsites(DBEnvObject* self, PyObject* args) +{ + int err; + int nsites; + + if (!PyArg_ParseTuple(args, "i:rep_set_nsites", &nsites)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_set_nsites(self->db_env, nsites); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_rep_get_nsites(DBEnvObject* self) +{ + int err; +#if (DBVER >= 47) + u_int32_t nsites; +#else + int nsites; +#endif + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_get_nsites(self->db_env, &nsites); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(nsites); +} + +static PyObject* +DBEnv_rep_set_priority(DBEnvObject* self, PyObject* args) +{ + int err; + int priority; + + if (!PyArg_ParseTuple(args, "i:rep_set_priority", &priority)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_set_priority(self->db_env, priority); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_rep_get_priority(DBEnvObject* self) +{ + int err; +#if (DBVER >= 47) + u_int32_t priority; +#else + int priority; +#endif + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_get_priority(self->db_env, &priority); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(priority); +} + +static PyObject* +DBEnv_rep_set_timeout(DBEnvObject* self, PyObject* args) +{ + int err; + int which, timeout; + + if (!PyArg_ParseTuple(args, "ii:rep_set_timeout", &which, &timeout)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_set_timeout(self->db_env, which, timeout); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_rep_get_timeout(DBEnvObject* self, PyObject* args) +{ + int err; + int which; + u_int32_t timeout; + + if (!PyArg_ParseTuple(args, "i:rep_get_timeout", &which)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->rep_get_timeout(self->db_env, which, &timeout); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(timeout); +} +#endif + +/* --------------------------------------------------------------------- */ +/* REPLICATION METHODS: Replication Manager */ + +#if (DBVER >= 45) +static PyObject* +DBEnv_repmgr_start(DBEnvObject* self, PyObject* args, PyObject* + kwargs) +{ + int err; + int nthreads, flags; + static char* kwnames[] = {"nthreads","flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "ii:repmgr_start", kwnames, &nthreads, &flags)) + { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_start(self->db_env, nthreads, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_repmgr_set_local_site(DBEnvObject* self, PyObject* args, PyObject* + kwargs) +{ + int err; + char *host; + int port; + int flags = 0; + static char* kwnames[] = {"host", "port", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "si|i:repmgr_set_local_site", kwnames, &host, &port, &flags)) + { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_set_local_site(self->db_env, host, port, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_repmgr_add_remote_site(DBEnvObject* self, PyObject* args, PyObject* + kwargs) +{ + int err; + char *host; + int port; + int flags = 0; + int eidp; + static char* kwnames[] = {"host", "port", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "si|i:repmgr_add_remote_site", kwnames, &host, &port, &flags)) + { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_add_remote_site(self->db_env, host, port, &eidp, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(eidp); +} + +static PyObject* +DBEnv_repmgr_set_ack_policy(DBEnvObject* self, PyObject* args) +{ + int err; + int ack_policy; + + if (!PyArg_ParseTuple(args, "i:repmgr_set_ack_policy", &ack_policy)) + { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_set_ack_policy(self->db_env, ack_policy); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_repmgr_get_ack_policy(DBEnvObject* self) +{ + int err; + int ack_policy; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_get_ack_policy(self->db_env, &ack_policy); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return NUMBER_FromLong(ack_policy); +} + +static PyObject* +DBEnv_repmgr_site_list(DBEnvObject* self) +{ + int err; + unsigned int countp; + DB_REPMGR_SITE *listp; + PyObject *stats, *key, *tuple; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_site_list(self->db_env, &countp, &listp); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + stats=PyDict_New(); + if (stats == NULL) { + free(listp); + return NULL; + } + + for(;countp--;) { + key=NUMBER_FromLong(listp[countp].eid); + if(!key) { + Py_DECREF(stats); + free(listp); + return NULL; + } +#if (PY_VERSION_HEX >= 0x02040000) + tuple=Py_BuildValue("(sII)", listp[countp].host, + listp[countp].port, listp[countp].status); +#else + tuple=Py_BuildValue("(sii)", listp[countp].host, + listp[countp].port, listp[countp].status); +#endif + if(!tuple) { + Py_DECREF(key); + Py_DECREF(stats); + free(listp); + return NULL; + } + if(PyDict_SetItem(stats, key, tuple)) { + Py_DECREF(key); + Py_DECREF(tuple); + Py_DECREF(stats); + free(listp); + return NULL; + } + } + free(listp); + return stats; +} +#endif + +#if (DBVER >= 46) +static PyObject* +DBEnv_repmgr_stat_print(DBEnvObject* self, PyObject* args, PyObject *kwargs) +{ + int err; + int flags=0; + static char* kwnames[] = { "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:repmgr_stat_print", + kwnames, &flags)) + { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_stat_print(self->db_env, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_repmgr_stat(DBEnvObject* self, PyObject* args, PyObject *kwargs) +{ + int err; + int flags=0; + DB_REPMGR_STAT *statp; + PyObject *stats; + static char* kwnames[] = { "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:repmgr_stat", + kwnames, &flags)) + { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->repmgr_stat(self->db_env, &statp, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + stats=PyDict_New(); + if (stats == NULL) { + free(statp); + return NULL; + } + +#define MAKE_ENTRY(name) _addIntToDict(stats, #name, statp->st_##name) + + MAKE_ENTRY(perm_failed); + MAKE_ENTRY(msgs_queued); + MAKE_ENTRY(msgs_dropped); + MAKE_ENTRY(connection_drop); + MAKE_ENTRY(connect_fail); + +#undef MAKE_ENTRY + + free(statp); + return stats; +} +#endif + + +/* --------------------------------------------------------------------- */ +/* DBTxn methods */ + + +static void _close_transaction_cursors(DBTxnObject* txn) +{ + PyObject *dummy; + + while(txn->children_cursors) { + PyErr_Warn(PyExc_RuntimeWarning, + "Must close cursors before resolving a transaction."); + dummy=DBC_close_internal(txn->children_cursors); + Py_XDECREF(dummy); + } +} + +static void _promote_transaction_dbs_and_sequences(DBTxnObject *txn) +{ + DBObject *db; +#if (DBVER >= 43) + DBSequenceObject *dbs; +#endif + + while (txn->children_dbs) { + db=txn->children_dbs; + EXTRACT_FROM_DOUBLE_LINKED_LIST_TXN(db); + if (txn->parent_txn) { + INSERT_IN_DOUBLE_LINKED_LIST_TXN(txn->parent_txn->children_dbs,db); + db->txn=txn->parent_txn; + } else { + /* The db is already linked to its environment, + ** so nothing to do. + */ + db->txn=NULL; + } + } + +#if (DBVER >= 43) + while (txn->children_sequences) { + dbs=txn->children_sequences; + EXTRACT_FROM_DOUBLE_LINKED_LIST_TXN(dbs); + if (txn->parent_txn) { + INSERT_IN_DOUBLE_LINKED_LIST_TXN(txn->parent_txn->children_sequences,dbs); + dbs->txn=txn->parent_txn; + } else { + /* The sequence is already linked to its + ** parent db. Nothing to do. + */ + dbs->txn=NULL; + } + } +#endif +} + + +static PyObject* +DBTxn_commit(DBTxnObject* self, PyObject* args) +{ + int flags=0, err; + DB_TXN *txn; + + if (!PyArg_ParseTuple(args, "|i:commit", &flags)) + return NULL; + + _close_transaction_cursors(self); + + if (!self->txn) { + PyObject *t = Py_BuildValue("(is)", 0, "DBTxn must not be used " + "after txn_commit, txn_abort " + "or txn_discard"); + if (t) { + PyErr_SetObject(DBError, t); + Py_DECREF(t); + } + return NULL; + } + self->flag_prepare=0; + txn = self->txn; + self->txn = NULL; /* this DB_TXN is no longer valid after this call */ + + EXTRACT_FROM_DOUBLE_LINKED_LIST(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = txn->commit(txn, flags); + MYDB_END_ALLOW_THREADS; + + _promote_transaction_dbs_and_sequences(self); + + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBTxn_prepare(DBTxnObject* self, PyObject* args) +{ + int err; + char* gid=NULL; + int gid_size=0; + + if (!PyArg_ParseTuple(args, "s#:prepare", &gid, &gid_size)) + return NULL; + + if (gid_size != DB_XIDDATASIZE) { + PyErr_SetString(PyExc_TypeError, + "gid must be DB_XIDDATASIZE bytes long"); + return NULL; + } + + if (!self->txn) { + PyObject *t = Py_BuildValue("(is)", 0,"DBTxn must not be used " + "after txn_commit, txn_abort " + "or txn_discard"); + if (t) { + PyErr_SetObject(DBError, t); + Py_DECREF(t); + } + return NULL; + } + self->flag_prepare=1; /* Prepare state */ + MYDB_BEGIN_ALLOW_THREADS; + err = self->txn->prepare(self->txn, (u_int8_t*)gid); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBTxn_abort_discard_internal(DBTxnObject* self, int discard) +{ + PyObject *dummy; + int err=0; + DB_TXN *txn; + + if (!self->txn) { + PyObject *t = Py_BuildValue("(is)", 0, "DBTxn must not be used " + "after txn_commit, txn_abort " + "or txn_discard"); + if (t) { + PyErr_SetObject(DBError, t); + Py_DECREF(t); + } + return NULL; + } + txn = self->txn; + self->txn = NULL; /* this DB_TXN is no longer valid after this call */ + + _close_transaction_cursors(self); +#if (DBVER >= 43) + while (self->children_sequences) { + dummy=DBSequence_close_internal(self->children_sequences,0,0); + Py_XDECREF(dummy); + } +#endif + while (self->children_dbs) { + dummy=DB_close_internal(self->children_dbs, 0, 0); + Py_XDECREF(dummy); + } + + EXTRACT_FROM_DOUBLE_LINKED_LIST(self); + + MYDB_BEGIN_ALLOW_THREADS; + if (discard) { + assert(!self->flag_prepare); + err = txn->discard(txn,0); + } else { + /* + ** If the transaction is in the "prepare" or "recover" state, + ** we better do not implicitly abort it. + */ + if (!self->flag_prepare) { + err = txn->abort(txn); + } + } + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBTxn_abort(DBTxnObject* self) +{ + self->flag_prepare=0; + _close_transaction_cursors(self); + + return DBTxn_abort_discard_internal(self,0); +} + +static PyObject* +DBTxn_discard(DBTxnObject* self) +{ + self->flag_prepare=0; + _close_transaction_cursors(self); + + return DBTxn_abort_discard_internal(self,1); +} + + +static PyObject* +DBTxn_id(DBTxnObject* self) +{ + int id; + + if (!self->txn) { + PyObject *t = Py_BuildValue("(is)", 0, "DBTxn must not be used " + "after txn_commit, txn_abort " + "or txn_discard"); + if (t) { + PyErr_SetObject(DBError, t); + Py_DECREF(t); + } + return NULL; + } + MYDB_BEGIN_ALLOW_THREADS; + id = self->txn->id(self->txn); + MYDB_END_ALLOW_THREADS; + return NUMBER_FromLong(id); +} + +#if (DBVER >= 43) +/* --------------------------------------------------------------------- */ +/* DBSequence methods */ + + +static PyObject* +DBSequence_close_internal(DBSequenceObject* self, int flags, int do_not_close) +{ + int err=0; + + if (self->sequence!=NULL) { + EXTRACT_FROM_DOUBLE_LINKED_LIST(self); + if (self->txn) { + EXTRACT_FROM_DOUBLE_LINKED_LIST_TXN(self); + self->txn=NULL; + } + + /* + ** "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 + ** "DBSequence.remove()" 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->sequence->close(self->sequence, flags); + MYDB_END_ALLOW_THREADS + } + self->sequence = NULL; + + RETURN_IF_ERR(); + } + + RETURN_NONE(); +} + +static PyObject* +DBSequence_close(DBSequenceObject* self, PyObject* args) +{ + int flags=0; + if (!PyArg_ParseTuple(args,"|i:close", &flags)) + return NULL; + + return DBSequence_close_internal(self,flags,0); +} + +static PyObject* +DBSequence_get(DBSequenceObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0; + int delta = 1; + db_seq_t value; + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + static char* kwnames[] = {"delta", "txn", "flags", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iOi:get", kwnames, &delta, &txnobj, &flags)) + return NULL; + CHECK_SEQUENCE_NOT_CLOSED(self) + + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->get(self->sequence, txn, delta, &value, flags); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + return PyLong_FromLongLong(value); +} + +static PyObject* +DBSequence_get_dbp(DBSequenceObject* self) +{ + CHECK_SEQUENCE_NOT_CLOSED(self) + Py_INCREF(self->mydb); + return (PyObject* )self->mydb; +} + +static PyObject* +DBSequence_get_key(DBSequenceObject* self) +{ + int err; + DBT key; + PyObject *retval = NULL; + + key.flags = DB_DBT_MALLOC; + CHECK_SEQUENCE_NOT_CLOSED(self) + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->get_key(self->sequence, &key); + MYDB_END_ALLOW_THREADS + + if (!err) + retval = Build_PyString(key.data, key.size); + + FREE_DBT(key); + RETURN_IF_ERR(); + + return retval; +} + +static PyObject* +DBSequence_init_value(DBSequenceObject* self, PyObject* args) +{ + int err; + PY_LONG_LONG value; + db_seq_t value2; + if (!PyArg_ParseTuple(args,"L:init_value", &value)) + return NULL; + CHECK_SEQUENCE_NOT_CLOSED(self) + + value2=value; /* If truncation, compiler should show a warning */ + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->initial_value(self->sequence, value2); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + + RETURN_NONE(); +} + +static PyObject* +DBSequence_open(DBSequenceObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0; + PyObject* keyobj; + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + DBT key; + + static char* kwnames[] = {"key", "txn", "flags", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oi:open", kwnames, &keyobj, &txnobj, &flags)) + return NULL; + + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->open(self->sequence, txn, &key, flags); + MYDB_END_ALLOW_THREADS + + FREE_DBT(key); + RETURN_IF_ERR(); + + if (txn) { + INSERT_IN_DOUBLE_LINKED_LIST_TXN(((DBTxnObject *)txnobj)->children_sequences,self); + self->txn=(DBTxnObject *)txnobj; + } + + RETURN_NONE(); +} + +static PyObject* +DBSequence_remove(DBSequenceObject* self, PyObject* args, PyObject* kwargs) +{ + PyObject *dummy; + int err, flags = 0; + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + + static char* kwnames[] = {"txn", "flags", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:remove", kwnames, &txnobj, &flags)) + return NULL; + + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + CHECK_SEQUENCE_NOT_CLOSED(self) + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->remove(self->sequence, txn, flags); + MYDB_END_ALLOW_THREADS + + dummy=DBSequence_close_internal(self,flags,1); + Py_XDECREF(dummy); + + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBSequence_set_cachesize(DBSequenceObject* self, PyObject* args) +{ + int err, size; + if (!PyArg_ParseTuple(args,"i:set_cachesize", &size)) + return NULL; + CHECK_SEQUENCE_NOT_CLOSED(self) + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->set_cachesize(self->sequence, size); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBSequence_get_cachesize(DBSequenceObject* self) +{ + int err, size; + + CHECK_SEQUENCE_NOT_CLOSED(self) + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->get_cachesize(self->sequence, &size); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + return NUMBER_FromLong(size); +} + +static PyObject* +DBSequence_set_flags(DBSequenceObject* self, PyObject* args) +{ + int err, flags = 0; + if (!PyArg_ParseTuple(args,"i:set_flags", &flags)) + return NULL; + CHECK_SEQUENCE_NOT_CLOSED(self) + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->set_flags(self->sequence, flags); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBSequence_get_flags(DBSequenceObject* self) +{ + unsigned int flags; + int err; + + CHECK_SEQUENCE_NOT_CLOSED(self) + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->get_flags(self->sequence, &flags); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + return NUMBER_FromLong((int)flags); +} + +static PyObject* +DBSequence_set_range(DBSequenceObject* self, PyObject* args) +{ + int err; + PY_LONG_LONG min, max; + db_seq_t min2, max2; + if (!PyArg_ParseTuple(args,"(LL):set_range", &min, &max)) + return NULL; + CHECK_SEQUENCE_NOT_CLOSED(self) + + min2=min; /* If truncation, compiler should show a warning */ + max2=max; + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->set_range(self->sequence, min2, max2); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBSequence_get_range(DBSequenceObject* self) +{ + int err; + PY_LONG_LONG min, max; + db_seq_t min2, max2; + + CHECK_SEQUENCE_NOT_CLOSED(self) + + MYDB_BEGIN_ALLOW_THREADS + err = self->sequence->get_range(self->sequence, &min2, &max2); + MYDB_END_ALLOW_THREADS + + RETURN_IF_ERR(); + min=min2; /* If truncation, compiler should show a warning */ + max=max2; + return Py_BuildValue("(LL)", min, max); +} + +static PyObject* +DBSequence_stat(DBSequenceObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0; + DB_SEQUENCE_STAT* sp = NULL; + PyObject* dict_stat; + static char* kwnames[] = {"flags", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:stat", kwnames, &flags)) + return NULL; + CHECK_SEQUENCE_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->sequence->stat(self->sequence, &sp, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + if ((dict_stat = PyDict_New()) == NULL) { + free(sp); + return NULL; + } + + +#define MAKE_INT_ENTRY(name) _addIntToDict(dict_stat, #name, sp->st_##name) +#define MAKE_LONG_LONG_ENTRY(name) _addDb_seq_tToDict(dict_stat, #name, sp->st_##name) + + MAKE_INT_ENTRY(wait); + MAKE_INT_ENTRY(nowait); + MAKE_LONG_LONG_ENTRY(current); + MAKE_LONG_LONG_ENTRY(value); + MAKE_LONG_LONG_ENTRY(last_value); + MAKE_LONG_LONG_ENTRY(min); + MAKE_LONG_LONG_ENTRY(max); + MAKE_INT_ENTRY(cache_size); + MAKE_INT_ENTRY(flags); + +#undef MAKE_INT_ENTRY +#undef MAKE_LONG_LONG_ENTRY + + free(sp); + return dict_stat; +} +#endif + + +/* --------------------------------------------------------------------- */ +/* Method definition tables and type objects */ + +static PyMethodDef DB_methods[] = { + {"append", (PyCFunction)DB_append, METH_VARARGS|METH_KEYWORDS}, + {"associate", (PyCFunction)DB_associate, METH_VARARGS|METH_KEYWORDS}, + {"close", (PyCFunction)DB_close, METH_VARARGS}, + {"consume", (PyCFunction)DB_consume, METH_VARARGS|METH_KEYWORDS}, + {"consume_wait", (PyCFunction)DB_consume_wait, METH_VARARGS|METH_KEYWORDS}, + {"cursor", (PyCFunction)DB_cursor, METH_VARARGS|METH_KEYWORDS}, + {"delete", (PyCFunction)DB_delete, METH_VARARGS|METH_KEYWORDS}, + {"fd", (PyCFunction)DB_fd, METH_NOARGS}, + {"get", (PyCFunction)DB_get, METH_VARARGS|METH_KEYWORDS}, + {"pget", (PyCFunction)DB_pget, METH_VARARGS|METH_KEYWORDS}, + {"get_both", (PyCFunction)DB_get_both, METH_VARARGS|METH_KEYWORDS}, + {"get_byteswapped", (PyCFunction)DB_get_byteswapped,METH_NOARGS}, + {"get_size", (PyCFunction)DB_get_size, METH_VARARGS|METH_KEYWORDS}, + {"get_type", (PyCFunction)DB_get_type, METH_NOARGS}, + {"join", (PyCFunction)DB_join, METH_VARARGS}, + {"key_range", (PyCFunction)DB_key_range, METH_VARARGS|METH_KEYWORDS}, + {"has_key", (PyCFunction)DB_has_key, METH_VARARGS|METH_KEYWORDS}, + {"items", (PyCFunction)DB_items, METH_VARARGS}, + {"keys", (PyCFunction)DB_keys, METH_VARARGS}, + {"open", (PyCFunction)DB_open, METH_VARARGS|METH_KEYWORDS}, + {"put", (PyCFunction)DB_put, METH_VARARGS|METH_KEYWORDS}, + {"remove", (PyCFunction)DB_remove, METH_VARARGS|METH_KEYWORDS}, + {"rename", (PyCFunction)DB_rename, METH_VARARGS}, + {"set_bt_minkey", (PyCFunction)DB_set_bt_minkey, METH_VARARGS}, + {"set_bt_compare", (PyCFunction)DB_set_bt_compare, METH_O}, + {"set_cachesize", (PyCFunction)DB_set_cachesize, METH_VARARGS}, +#if (DBVER >= 41) + {"set_encrypt", (PyCFunction)DB_set_encrypt, METH_VARARGS|METH_KEYWORDS}, +#endif + {"set_flags", (PyCFunction)DB_set_flags, METH_VARARGS}, + {"set_h_ffactor", (PyCFunction)DB_set_h_ffactor, METH_VARARGS}, + {"set_h_nelem", (PyCFunction)DB_set_h_nelem, METH_VARARGS}, + {"set_lorder", (PyCFunction)DB_set_lorder, METH_VARARGS}, + {"set_pagesize", (PyCFunction)DB_set_pagesize, METH_VARARGS}, + {"set_re_delim", (PyCFunction)DB_set_re_delim, METH_VARARGS}, + {"set_re_len", (PyCFunction)DB_set_re_len, METH_VARARGS}, + {"set_re_pad", (PyCFunction)DB_set_re_pad, METH_VARARGS}, + {"set_re_source", (PyCFunction)DB_set_re_source, METH_VARARGS}, + {"set_q_extentsize",(PyCFunction)DB_set_q_extentsize, METH_VARARGS}, + {"set_private", (PyCFunction)DB_set_private, METH_O}, + {"get_private", (PyCFunction)DB_get_private, METH_NOARGS}, + {"stat", (PyCFunction)DB_stat, METH_VARARGS|METH_KEYWORDS}, + {"sync", (PyCFunction)DB_sync, METH_VARARGS}, + {"truncate", (PyCFunction)DB_truncate, METH_VARARGS|METH_KEYWORDS}, + {"type", (PyCFunction)DB_get_type, METH_NOARGS}, + {"upgrade", (PyCFunction)DB_upgrade, METH_VARARGS}, + {"values", (PyCFunction)DB_values, METH_VARARGS}, + {"verify", (PyCFunction)DB_verify, METH_VARARGS|METH_KEYWORDS}, + {"set_get_returns_none",(PyCFunction)DB_set_get_returns_none, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static PyMappingMethods DB_mapping = { + DB_length, /*mp_length*/ + (binaryfunc)DB_subscript, /*mp_subscript*/ + (objobjargproc)DB_ass_sub, /*mp_ass_subscript*/ +}; + + +static PyMethodDef DBCursor_methods[] = { + {"close", (PyCFunction)DBC_close, METH_NOARGS}, + {"count", (PyCFunction)DBC_count, METH_VARARGS}, + {"current", (PyCFunction)DBC_current, METH_VARARGS|METH_KEYWORDS}, + {"delete", (PyCFunction)DBC_delete, METH_VARARGS}, + {"dup", (PyCFunction)DBC_dup, METH_VARARGS}, + {"first", (PyCFunction)DBC_first, METH_VARARGS|METH_KEYWORDS}, + {"get", (PyCFunction)DBC_get, METH_VARARGS|METH_KEYWORDS}, + {"pget", (PyCFunction)DBC_pget, METH_VARARGS|METH_KEYWORDS}, + {"get_recno", (PyCFunction)DBC_get_recno, METH_NOARGS}, + {"last", (PyCFunction)DBC_last, METH_VARARGS|METH_KEYWORDS}, + {"next", (PyCFunction)DBC_next, METH_VARARGS|METH_KEYWORDS}, + {"prev", (PyCFunction)DBC_prev, METH_VARARGS|METH_KEYWORDS}, + {"put", (PyCFunction)DBC_put, METH_VARARGS|METH_KEYWORDS}, + {"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}, + {"get_current_size",(PyCFunction)DBC_get_current_size, METH_NOARGS}, + {"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}, + {"next_nodup", (PyCFunction)DBC_next_nodup, METH_VARARGS|METH_KEYWORDS}, + {"prev_nodup", (PyCFunction)DBC_prev_nodup, METH_VARARGS|METH_KEYWORDS}, + {"join_item", (PyCFunction)DBC_join_item, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static PyMethodDef DBEnv_methods[] = { + {"close", (PyCFunction)DBEnv_close, METH_VARARGS}, + {"open", (PyCFunction)DBEnv_open, METH_VARARGS}, + {"remove", (PyCFunction)DBEnv_remove, METH_VARARGS}, +#if (DBVER >= 41) + {"dbremove", (PyCFunction)DBEnv_dbremove, METH_VARARGS|METH_KEYWORDS}, + {"dbrename", (PyCFunction)DBEnv_dbrename, METH_VARARGS|METH_KEYWORDS}, + {"set_encrypt", (PyCFunction)DBEnv_set_encrypt, METH_VARARGS|METH_KEYWORDS}, +#endif + {"set_timeout", (PyCFunction)DBEnv_set_timeout, METH_VARARGS|METH_KEYWORDS}, + {"set_shm_key", (PyCFunction)DBEnv_set_shm_key, METH_VARARGS}, + {"set_cachesize", (PyCFunction)DBEnv_set_cachesize, METH_VARARGS}, + {"set_data_dir", (PyCFunction)DBEnv_set_data_dir, METH_VARARGS}, + {"set_flags", (PyCFunction)DBEnv_set_flags, METH_VARARGS}, +#if (DBVER >= 47) + {"log_set_config", (PyCFunction)DBEnv_log_set_config, METH_VARARGS}, +#endif + {"set_lg_bsize", (PyCFunction)DBEnv_set_lg_bsize, METH_VARARGS}, + {"set_lg_dir", (PyCFunction)DBEnv_set_lg_dir, METH_VARARGS}, + {"set_lg_max", (PyCFunction)DBEnv_set_lg_max, METH_VARARGS}, +#if (DBVER >= 42) + {"get_lg_max", (PyCFunction)DBEnv_get_lg_max, METH_NOARGS}, +#endif + {"set_lg_regionmax",(PyCFunction)DBEnv_set_lg_regionmax, METH_VARARGS}, + {"set_lk_detect", (PyCFunction)DBEnv_set_lk_detect, METH_VARARGS}, +#if (DBVER < 45) + {"set_lk_max", (PyCFunction)DBEnv_set_lk_max, METH_VARARGS}, +#endif + {"set_lk_max_locks", (PyCFunction)DBEnv_set_lk_max_locks, METH_VARARGS}, + {"set_lk_max_lockers", (PyCFunction)DBEnv_set_lk_max_lockers, METH_VARARGS}, + {"set_lk_max_objects", (PyCFunction)DBEnv_set_lk_max_objects, METH_VARARGS}, + {"set_mp_mmapsize", (PyCFunction)DBEnv_set_mp_mmapsize, METH_VARARGS}, + {"set_tmp_dir", (PyCFunction)DBEnv_set_tmp_dir, METH_VARARGS}, + {"txn_begin", (PyCFunction)DBEnv_txn_begin, METH_VARARGS|METH_KEYWORDS}, + {"txn_checkpoint", (PyCFunction)DBEnv_txn_checkpoint, METH_VARARGS}, + {"txn_stat", (PyCFunction)DBEnv_txn_stat, METH_VARARGS}, + {"set_tx_max", (PyCFunction)DBEnv_set_tx_max, METH_VARARGS}, + {"set_tx_timestamp", (PyCFunction)DBEnv_set_tx_timestamp, METH_VARARGS}, + {"lock_detect", (PyCFunction)DBEnv_lock_detect, METH_VARARGS}, + {"lock_get", (PyCFunction)DBEnv_lock_get, METH_VARARGS}, + {"lock_id", (PyCFunction)DBEnv_lock_id, METH_NOARGS}, + {"lock_id_free", (PyCFunction)DBEnv_lock_id_free, METH_VARARGS}, + {"lock_put", (PyCFunction)DBEnv_lock_put, METH_VARARGS}, + {"lock_stat", (PyCFunction)DBEnv_lock_stat, METH_VARARGS}, + {"log_archive", (PyCFunction)DBEnv_log_archive, METH_VARARGS}, + {"log_flush", (PyCFunction)DBEnv_log_flush, METH_NOARGS}, + {"log_stat", (PyCFunction)DBEnv_log_stat, METH_VARARGS}, +#if (DBVER >= 44) + {"lsn_reset", (PyCFunction)DBEnv_lsn_reset, METH_VARARGS|METH_KEYWORDS}, +#endif + {"set_get_returns_none",(PyCFunction)DBEnv_set_get_returns_none, METH_VARARGS}, + {"txn_recover", (PyCFunction)DBEnv_txn_recover, METH_NOARGS}, + {"set_rpc_server", (PyCFunction)DBEnv_set_rpc_server, + METH_VARARGS||METH_KEYWORDS}, + {"set_verbose", (PyCFunction)DBEnv_set_verbose, METH_VARARGS}, +#if (DBVER >= 42) + {"get_verbose", (PyCFunction)DBEnv_get_verbose, METH_VARARGS}, +#endif + {"set_private", (PyCFunction)DBEnv_set_private, METH_O}, + {"get_private", (PyCFunction)DBEnv_get_private, METH_NOARGS}, + {"rep_start", (PyCFunction)DBEnv_rep_start, + METH_VARARGS|METH_KEYWORDS}, + {"rep_set_transport", (PyCFunction)DBEnv_rep_set_transport, METH_VARARGS}, + {"rep_process_message", (PyCFunction)DBEnv_rep_process_message, + METH_VARARGS}, +#if (DBVER >= 46) + {"rep_elect", (PyCFunction)DBEnv_rep_elect, METH_VARARGS}, +#endif +#if (DBVER >= 44) + {"rep_set_config", (PyCFunction)DBEnv_rep_set_config, METH_VARARGS}, + {"rep_get_config", (PyCFunction)DBEnv_rep_get_config, METH_VARARGS}, + {"rep_sync", (PyCFunction)DBEnv_rep_sync, METH_NOARGS}, +#endif +#if (DBVER >= 45) + {"rep_set_limit", (PyCFunction)DBEnv_rep_set_limit, METH_VARARGS}, + {"rep_get_limit", (PyCFunction)DBEnv_rep_get_limit, METH_NOARGS}, +#endif +#if (DBVER >= 47) + {"rep_set_request", (PyCFunction)DBEnv_rep_set_request, METH_VARARGS}, + {"rep_get_request", (PyCFunction)DBEnv_rep_get_request, METH_NOARGS}, +#endif +#if (DBVER >= 45) + {"set_event_notify", (PyCFunction)DBEnv_set_event_notify, METH_O}, +#endif +#if (DBVER >= 45) + {"rep_set_nsites", (PyCFunction)DBEnv_rep_set_nsites, METH_VARARGS}, + {"rep_get_nsites", (PyCFunction)DBEnv_rep_get_nsites, METH_NOARGS}, + {"rep_set_priority", (PyCFunction)DBEnv_rep_set_priority, METH_VARARGS}, + {"rep_get_priority", (PyCFunction)DBEnv_rep_get_priority, METH_NOARGS}, + {"rep_set_timeout", (PyCFunction)DBEnv_rep_set_timeout, METH_VARARGS}, + {"rep_get_timeout", (PyCFunction)DBEnv_rep_get_timeout, METH_VARARGS}, +#endif +#if (DBVER >= 45) + {"repmgr_start", (PyCFunction)DBEnv_repmgr_start, + METH_VARARGS|METH_KEYWORDS}, + {"repmgr_set_local_site", (PyCFunction)DBEnv_repmgr_set_local_site, + METH_VARARGS|METH_KEYWORDS}, + {"repmgr_add_remote_site", (PyCFunction)DBEnv_repmgr_add_remote_site, + METH_VARARGS|METH_KEYWORDS}, + {"repmgr_set_ack_policy", (PyCFunction)DBEnv_repmgr_set_ack_policy, + METH_VARARGS}, + {"repmgr_get_ack_policy", (PyCFunction)DBEnv_repmgr_get_ack_policy, + METH_NOARGS}, + {"repmgr_site_list", (PyCFunction)DBEnv_repmgr_site_list, + METH_NOARGS}, +#endif +#if (DBVER >= 46) + {"repmgr_stat", (PyCFunction)DBEnv_repmgr_stat, + METH_VARARGS|METH_KEYWORDS}, + {"repmgr_stat_print", (PyCFunction)DBEnv_repmgr_stat_print, + METH_VARARGS|METH_KEYWORDS}, +#endif + {NULL, NULL} /* sentinel */ +}; + + +static PyMethodDef DBTxn_methods[] = { + {"commit", (PyCFunction)DBTxn_commit, METH_VARARGS}, + {"prepare", (PyCFunction)DBTxn_prepare, METH_VARARGS}, + {"discard", (PyCFunction)DBTxn_discard, METH_NOARGS}, + {"abort", (PyCFunction)DBTxn_abort, METH_NOARGS}, + {"id", (PyCFunction)DBTxn_id, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + + +#if (DBVER >= 43) +static PyMethodDef DBSequence_methods[] = { + {"close", (PyCFunction)DBSequence_close, METH_VARARGS}, + {"get", (PyCFunction)DBSequence_get, METH_VARARGS|METH_KEYWORDS}, + {"get_dbp", (PyCFunction)DBSequence_get_dbp, METH_NOARGS}, + {"get_key", (PyCFunction)DBSequence_get_key, METH_NOARGS}, + {"init_value", (PyCFunction)DBSequence_init_value, METH_VARARGS}, + {"open", (PyCFunction)DBSequence_open, METH_VARARGS|METH_KEYWORDS}, + {"remove", (PyCFunction)DBSequence_remove, METH_VARARGS|METH_KEYWORDS}, + {"set_cachesize", (PyCFunction)DBSequence_set_cachesize, METH_VARARGS}, + {"get_cachesize", (PyCFunction)DBSequence_get_cachesize, METH_NOARGS}, + {"set_flags", (PyCFunction)DBSequence_set_flags, METH_VARARGS}, + {"get_flags", (PyCFunction)DBSequence_get_flags, METH_NOARGS}, + {"set_range", (PyCFunction)DBSequence_set_range, METH_VARARGS}, + {"get_range", (PyCFunction)DBSequence_get_range, METH_NOARGS}, + {"stat", (PyCFunction)DBSequence_stat, METH_VARARGS|METH_KEYWORDS}, + {NULL, NULL} /* sentinel */ +}; +#endif + + +static PyObject* +DBEnv_db_home_get(DBEnvObject* self) +{ + const char *home = NULL; + + CHECK_ENV_NOT_CLOSED(self); + +#if (DBVER >= 42) + self->db_env->get_home(self->db_env, &home); +#else + home=self->db_env->db_home; +#endif + + if (home == NULL) { + RETURN_NONE(); + } + return PyBytes_FromString(home); +} + +static PyGetSetDef DBEnv_getsets[] = { + {"db_home", (getter)DBEnv_db_home_get, NULL,}, + {NULL} +}; + + +statichere PyTypeObject DB_Type = { +#if (PY_VERSION_HEX < 0x03000000) + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ +#else + PyVarObject_HEAD_INIT(NULL, 0) +#endif + "DB", /*tp_name*/ + sizeof(DBObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DB_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &DB_mapping,/*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if (PY_VERSION_HEX < 0x03000000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(DBObject, in_weakreflist), /* tp_weaklistoffset */ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + DB_methods, /*tp_methods*/ + 0, /*tp_members*/ +}; + + +statichere PyTypeObject DBCursor_Type = { +#if (PY_VERSION_HEX < 0x03000000) + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ +#else + PyVarObject_HEAD_INIT(NULL, 0) +#endif + "DBCursor", /*tp_name*/ + sizeof(DBCursorObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBCursor_dealloc,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ +#if (PY_VERSION_HEX < 0x03000000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(DBCursorObject, in_weakreflist), /* tp_weaklistoffset */ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + DBCursor_methods, /*tp_methods*/ + 0, /*tp_members*/ +}; + + +statichere PyTypeObject DBEnv_Type = { +#if (PY_VERSION_HEX < 0x03000000) + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ +#else + PyVarObject_HEAD_INIT(NULL, 0) +#endif + "DBEnv", /*tp_name*/ + sizeof(DBEnvObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBEnv_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if (PY_VERSION_HEX < 0x03000000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(DBEnvObject, in_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + DBEnv_methods, /* tp_methods */ + 0, /* tp_members */ + DBEnv_getsets, /* tp_getsets */ +}; + +statichere PyTypeObject DBTxn_Type = { +#if (PY_VERSION_HEX < 0x03000000) + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ +#else + PyVarObject_HEAD_INIT(NULL, 0) +#endif + "DBTxn", /*tp_name*/ + sizeof(DBTxnObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBTxn_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if (PY_VERSION_HEX < 0x03000000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(DBTxnObject, in_weakreflist), /* tp_weaklistoffset */ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + DBTxn_methods, /*tp_methods*/ + 0, /*tp_members*/ +}; + + +statichere PyTypeObject DBLock_Type = { +#if (PY_VERSION_HEX < 0x03000000) + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ +#else + PyVarObject_HEAD_INIT(NULL, 0) +#endif + "DBLock", /*tp_name*/ + sizeof(DBLockObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBLock_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if (PY_VERSION_HEX < 0x03000000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(DBLockObject, in_weakreflist), /* tp_weaklistoffset */ +}; + +#if (DBVER >= 43) +statichere PyTypeObject DBSequence_Type = { +#if (PY_VERSION_HEX < 0x03000000) + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ +#else + PyVarObject_HEAD_INIT(NULL, 0) +#endif + "DBSequence", /*tp_name*/ + sizeof(DBSequenceObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBSequence_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if (PY_VERSION_HEX < 0x03000000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(DBSequenceObject, in_weakreflist), /* tp_weaklistoffset */ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + DBSequence_methods, /*tp_methods*/ + 0, /*tp_members*/ +}; +#endif + +/* --------------------------------------------------------------------- */ +/* Module-level functions */ + +static PyObject* +DB_construct(PyObject* self, PyObject* args, PyObject* kwargs) +{ + PyObject* dbenvobj = NULL; + int flags = 0; + static char* kwnames[] = { "dbEnv", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:DB", kwnames, + &dbenvobj, &flags)) + return NULL; + if (dbenvobj == Py_None) + dbenvobj = NULL; + else if (dbenvobj && !DBEnvObject_Check(dbenvobj)) { + makeTypeError("DBEnv", dbenvobj); + return NULL; + } + + return (PyObject* )newDBObject((DBEnvObject*)dbenvobj, flags); +} + + +static PyObject* +DBEnv_construct(PyObject* self, PyObject* args) +{ + int flags = 0; + if (!PyArg_ParseTuple(args, "|i:DbEnv", &flags)) return NULL; + return (PyObject* )newDBEnvObject(flags); +} + +#if (DBVER >= 43) +static PyObject* +DBSequence_construct(PyObject* self, PyObject* args, PyObject* kwargs) +{ + PyObject* dbobj; + int flags = 0; + static char* kwnames[] = { "db", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:DBSequence", kwnames, &dbobj, &flags)) + return NULL; + if (!DBObject_Check(dbobj)) { + makeTypeError("DB", dbobj); + return NULL; + } + return (PyObject* )newDBSequenceObject((DBObject*)dbobj, flags); +} +#endif + +static char bsddb_version_doc[] = +"Returns a tuple of major, minor, and patch release numbers of the\n\ +underlying DB library."; + +static PyObject* +bsddb_version(PyObject* self) +{ + int major, minor, patch; + + db_version(&major, &minor, &patch); + return Py_BuildValue("(iii)", major, minor, patch); +} + + +/* List of functions defined in the module */ +static PyMethodDef bsddb_methods[] = { + {"DB", (PyCFunction)DB_construct, METH_VARARGS | METH_KEYWORDS }, + {"DBEnv", (PyCFunction)DBEnv_construct, METH_VARARGS}, +#if (DBVER >= 43) + {"DBSequence", (PyCFunction)DBSequence_construct, METH_VARARGS | METH_KEYWORDS }, +#endif + {"version", (PyCFunction)bsddb_version, METH_NOARGS, bsddb_version_doc}, + {NULL, NULL} /* sentinel */ +}; + + +/* API structure */ +static BSDDB_api bsddb_api; + + +/* --------------------------------------------------------------------- */ +/* Module initialization */ + + +/* Convenience routine to export an integer value. + * Errors are silently ignored, for better or for worse... + */ +#define ADD_INT(dict, NAME) _addIntToDict(dict, #NAME, NAME) + +#define MODULE_NAME_MAX_LEN 11 +static char _bsddbModuleName[MODULE_NAME_MAX_LEN+1] = "_bsddb"; + +#if (PY_VERSION_HEX >= 0x03000000) +static struct PyModuleDef bsddbmodule = { + PyModuleDef_HEAD_INIT, + _bsddbModuleName, /* Name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + bsddb_methods, + NULL, /* Reload */ + NULL, /* Traverse */ + NULL, /* Clear */ + NULL /* Free */ +}; +#endif + + +#if (PY_VERSION_HEX < 0x03000000) +DL_EXPORT(void) init_bsddb(void) +#else +PyMODINIT_FUNC PyInit__bsddb(void) /* Note the two underscores */ +#endif +{ + PyObject* m; + PyObject* d; + PyObject* pybsddb_version_s = PyBytes_FromString( PY_BSDDB_VERSION ); + PyObject* db_version_s = PyBytes_FromString( DB_VERSION_STRING ); + PyObject* cvsid_s = PyBytes_FromString( rcs_id ); + PyObject* py_api; + + /* Initialize object types */ + if ((PyType_Ready(&DB_Type) < 0) + || (PyType_Ready(&DBCursor_Type) < 0) + || (PyType_Ready(&DBEnv_Type) < 0) + || (PyType_Ready(&DBTxn_Type) < 0) + || (PyType_Ready(&DBLock_Type) < 0) +#if (DBVER >= 43) + || (PyType_Ready(&DBSequence_Type) < 0) +#endif + ) { +#if (PY_VERSION_HEX < 0x03000000) + return; +#else + return NULL; +#endif + } + +#if defined(WITH_THREAD) && !defined(MYDB_USE_GILSTATE) + /* Save the current interpreter, so callbacks can do the right thing. */ + _db_interpreterState = PyThreadState_GET()->interp; +#endif + + /* Create the module and add the functions */ +#if (PY_VERSION_HEX < 0x03000000) + m = Py_InitModule(_bsddbModuleName, bsddb_methods); +#else + m=PyModule_Create(&bsddbmodule); +#endif + if (m == NULL) { +#if (PY_VERSION_HEX < 0x03000000) + return; +#else + return NULL; +#endif + } + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + PyDict_SetItemString(d, "__version__", pybsddb_version_s); + PyDict_SetItemString(d, "cvsid", cvsid_s); + PyDict_SetItemString(d, "DB_VERSION_STRING", db_version_s); + Py_DECREF(pybsddb_version_s); + pybsddb_version_s = NULL; + Py_DECREF(cvsid_s); + cvsid_s = NULL; + Py_DECREF(db_version_s); + db_version_s = NULL; + + ADD_INT(d, DB_VERSION_MAJOR); + ADD_INT(d, DB_VERSION_MINOR); + ADD_INT(d, DB_VERSION_PATCH); + + ADD_INT(d, DB_MAX_PAGES); + ADD_INT(d, DB_MAX_RECORDS); + +#if (DBVER >= 42) + ADD_INT(d, DB_RPCCLIENT); +#else + ADD_INT(d, DB_CLIENT); + /* allow apps to be written using DB_RPCCLIENT on older Berkeley DB */ + _addIntToDict(d, "DB_RPCCLIENT", DB_CLIENT); +#endif + ADD_INT(d, DB_XA_CREATE); + + ADD_INT(d, DB_CREATE); + ADD_INT(d, DB_NOMMAP); + ADD_INT(d, DB_THREAD); +#if (DBVER >= 45) + ADD_INT(d, DB_MULTIVERSION); +#endif + + ADD_INT(d, DB_FORCE); + ADD_INT(d, DB_INIT_CDB); + ADD_INT(d, DB_INIT_LOCK); + ADD_INT(d, DB_INIT_LOG); + ADD_INT(d, DB_INIT_MPOOL); + ADD_INT(d, DB_INIT_TXN); + ADD_INT(d, DB_JOINENV); + + ADD_INT(d, DB_XIDDATASIZE); + + ADD_INT(d, DB_RECOVER); + ADD_INT(d, DB_RECOVER_FATAL); + ADD_INT(d, DB_TXN_NOSYNC); + ADD_INT(d, DB_USE_ENVIRON); + ADD_INT(d, DB_USE_ENVIRON_ROOT); + + ADD_INT(d, DB_LOCKDOWN); + ADD_INT(d, DB_PRIVATE); + ADD_INT(d, DB_SYSTEM_MEM); + + ADD_INT(d, DB_TXN_SYNC); + ADD_INT(d, DB_TXN_NOWAIT); + + ADD_INT(d, DB_EXCL); + ADD_INT(d, DB_FCNTL_LOCKING); + ADD_INT(d, DB_ODDFILESIZE); + ADD_INT(d, DB_RDWRMASTER); + ADD_INT(d, DB_RDONLY); + ADD_INT(d, DB_TRUNCATE); + ADD_INT(d, DB_EXTENT); + ADD_INT(d, DB_CDB_ALLDB); + ADD_INT(d, DB_VERIFY); + ADD_INT(d, DB_UPGRADE); + + ADD_INT(d, DB_AGGRESSIVE); + ADD_INT(d, DB_NOORDERCHK); + ADD_INT(d, DB_ORDERCHKONLY); + ADD_INT(d, DB_PR_PAGE); + + ADD_INT(d, DB_PR_RECOVERYTEST); + ADD_INT(d, DB_SALVAGE); + + ADD_INT(d, DB_LOCK_NORUN); + ADD_INT(d, DB_LOCK_DEFAULT); + ADD_INT(d, DB_LOCK_OLDEST); + ADD_INT(d, DB_LOCK_RANDOM); + ADD_INT(d, DB_LOCK_YOUNGEST); + ADD_INT(d, DB_LOCK_MAXLOCKS); + ADD_INT(d, DB_LOCK_MINLOCKS); + ADD_INT(d, DB_LOCK_MINWRITE); + + ADD_INT(d, DB_LOCK_EXPIRE); +#if (DBVER >= 43) + ADD_INT(d, DB_LOCK_MAXWRITE); +#endif + + _addIntToDict(d, "DB_LOCK_CONFLICT", 0); + + ADD_INT(d, DB_LOCK_DUMP); + ADD_INT(d, DB_LOCK_GET); + ADD_INT(d, DB_LOCK_INHERIT); + ADD_INT(d, DB_LOCK_PUT); + ADD_INT(d, DB_LOCK_PUT_ALL); + ADD_INT(d, DB_LOCK_PUT_OBJ); + + ADD_INT(d, DB_LOCK_NG); + ADD_INT(d, DB_LOCK_READ); + ADD_INT(d, DB_LOCK_WRITE); + ADD_INT(d, DB_LOCK_NOWAIT); + ADD_INT(d, DB_LOCK_WAIT); + ADD_INT(d, DB_LOCK_IWRITE); + ADD_INT(d, DB_LOCK_IREAD); + ADD_INT(d, DB_LOCK_IWR); +#if (DBVER < 44) + ADD_INT(d, DB_LOCK_DIRTY); +#else + ADD_INT(d, DB_LOCK_READ_UNCOMMITTED); /* renamed in 4.4 */ +#endif + ADD_INT(d, DB_LOCK_WWRITE); + + ADD_INT(d, DB_LOCK_RECORD); + ADD_INT(d, DB_LOCK_UPGRADE); + ADD_INT(d, DB_LOCK_SWITCH); + ADD_INT(d, DB_LOCK_UPGRADE_WRITE); + + ADD_INT(d, DB_LOCK_NOWAIT); + ADD_INT(d, DB_LOCK_RECORD); + ADD_INT(d, DB_LOCK_UPGRADE); + + ADD_INT(d, DB_LSTAT_ABORTED); +#if (DBVER < 43) + ADD_INT(d, DB_LSTAT_ERR); +#endif + ADD_INT(d, DB_LSTAT_FREE); + ADD_INT(d, DB_LSTAT_HELD); + + ADD_INT(d, DB_LSTAT_PENDING); + ADD_INT(d, DB_LSTAT_WAITING); + + ADD_INT(d, DB_ARCH_ABS); + ADD_INT(d, DB_ARCH_DATA); + ADD_INT(d, DB_ARCH_LOG); +#if (DBVER >= 42) + ADD_INT(d, DB_ARCH_REMOVE); +#endif + + ADD_INT(d, DB_BTREE); + ADD_INT(d, DB_HASH); + ADD_INT(d, DB_RECNO); + ADD_INT(d, DB_QUEUE); + ADD_INT(d, DB_UNKNOWN); + + ADD_INT(d, DB_DUP); + ADD_INT(d, DB_DUPSORT); + ADD_INT(d, DB_RECNUM); + ADD_INT(d, DB_RENUMBER); + ADD_INT(d, DB_REVSPLITOFF); + ADD_INT(d, DB_SNAPSHOT); + + ADD_INT(d, DB_JOIN_NOSORT); + + ADD_INT(d, DB_AFTER); + ADD_INT(d, DB_APPEND); + ADD_INT(d, DB_BEFORE); +#if (DBVER < 45) + ADD_INT(d, DB_CACHED_COUNTS); +#endif + +#if (DBVER >= 41) + _addIntToDict(d, "DB_CHECKPOINT", 0); +#else + ADD_INT(d, DB_CHECKPOINT); + ADD_INT(d, DB_CURLSN); +#endif +#if (DBVER <= 41) + ADD_INT(d, DB_COMMIT); +#endif + ADD_INT(d, DB_CONSUME); + ADD_INT(d, DB_CONSUME_WAIT); + ADD_INT(d, DB_CURRENT); + ADD_INT(d, DB_FAST_STAT); + ADD_INT(d, DB_FIRST); + ADD_INT(d, DB_FLUSH); + ADD_INT(d, DB_GET_BOTH); + ADD_INT(d, DB_GET_RECNO); + ADD_INT(d, DB_JOIN_ITEM); + ADD_INT(d, DB_KEYFIRST); + ADD_INT(d, DB_KEYLAST); + ADD_INT(d, DB_LAST); + ADD_INT(d, DB_NEXT); + ADD_INT(d, DB_NEXT_DUP); + ADD_INT(d, DB_NEXT_NODUP); + ADD_INT(d, DB_NODUPDATA); + ADD_INT(d, DB_NOOVERWRITE); + ADD_INT(d, DB_NOSYNC); + ADD_INT(d, DB_POSITION); + ADD_INT(d, DB_PREV); + ADD_INT(d, DB_PREV_NODUP); +#if (DBVER < 45) + ADD_INT(d, DB_RECORDCOUNT); +#endif + ADD_INT(d, DB_SET); + ADD_INT(d, DB_SET_RANGE); + ADD_INT(d, DB_SET_RECNO); + ADD_INT(d, DB_WRITECURSOR); + + ADD_INT(d, DB_OPFLAGS_MASK); + ADD_INT(d, DB_RMW); + ADD_INT(d, DB_DIRTY_READ); + ADD_INT(d, DB_MULTIPLE); + ADD_INT(d, DB_MULTIPLE_KEY); + +#if (DBVER >= 44) + ADD_INT(d, DB_READ_UNCOMMITTED); /* replaces DB_DIRTY_READ in 4.4 */ + ADD_INT(d, DB_READ_COMMITTED); +#endif + + ADD_INT(d, DB_DONOTINDEX); + +#if (DBVER >= 41) + _addIntToDict(d, "DB_INCOMPLETE", 0); +#else + ADD_INT(d, DB_INCOMPLETE); +#endif + ADD_INT(d, DB_KEYEMPTY); + ADD_INT(d, DB_KEYEXIST); + ADD_INT(d, DB_LOCK_DEADLOCK); + ADD_INT(d, DB_LOCK_NOTGRANTED); + ADD_INT(d, DB_NOSERVER); + ADD_INT(d, DB_NOSERVER_HOME); + ADD_INT(d, DB_NOSERVER_ID); + ADD_INT(d, DB_NOTFOUND); + ADD_INT(d, DB_OLD_VERSION); + ADD_INT(d, DB_RUNRECOVERY); + ADD_INT(d, DB_VERIFY_BAD); + ADD_INT(d, DB_PAGE_NOTFOUND); + ADD_INT(d, DB_SECONDARY_BAD); + ADD_INT(d, DB_STAT_CLEAR); + ADD_INT(d, DB_REGION_INIT); + ADD_INT(d, DB_NOLOCKING); + ADD_INT(d, DB_YIELDCPU); + ADD_INT(d, DB_PANIC_ENVIRONMENT); + ADD_INT(d, DB_NOPANIC); + +#if (DBVER >= 41) + ADD_INT(d, DB_OVERWRITE); +#endif + +#ifdef DB_REGISTER + ADD_INT(d, DB_REGISTER); +#endif + +#if (DBVER >= 42) + ADD_INT(d, DB_TIME_NOTGRANTED); + ADD_INT(d, DB_TXN_NOT_DURABLE); + ADD_INT(d, DB_TXN_WRITE_NOSYNC); + ADD_INT(d, DB_DIRECT_DB); + ADD_INT(d, DB_INIT_REP); + ADD_INT(d, DB_ENCRYPT); + ADD_INT(d, DB_CHKSUM); +#endif + +#if (DBVER >= 42) && (DBVER < 47) + ADD_INT(d, DB_LOG_AUTOREMOVE); + ADD_INT(d, DB_DIRECT_LOG); +#endif + +#if (DBVER >= 47) + ADD_INT(d, DB_LOG_DIRECT); + ADD_INT(d, DB_LOG_DSYNC); + ADD_INT(d, DB_LOG_IN_MEMORY); + ADD_INT(d, DB_LOG_AUTO_REMOVE); + ADD_INT(d, DB_LOG_ZERO); +#endif + +#if (DBVER >= 44) + ADD_INT(d, DB_DSYNC_DB); +#endif + +#if (DBVER >= 45) + ADD_INT(d, DB_TXN_SNAPSHOT); +#endif + + ADD_INT(d, DB_VERB_DEADLOCK); +#if (DBVER >= 46) + ADD_INT(d, DB_VERB_FILEOPS); + ADD_INT(d, DB_VERB_FILEOPS_ALL); +#endif + ADD_INT(d, DB_VERB_RECOVERY); +#if (DBVER >= 44) + ADD_INT(d, DB_VERB_REGISTER); +#endif + ADD_INT(d, DB_VERB_REPLICATION); + ADD_INT(d, DB_VERB_WAITSFOR); + +#if (DBVER >= 45) + ADD_INT(d, DB_EVENT_PANIC); + ADD_INT(d, DB_EVENT_REP_CLIENT); +#if (DBVER >= 46) + ADD_INT(d, DB_EVENT_REP_ELECTED); +#endif + ADD_INT(d, DB_EVENT_REP_MASTER); + ADD_INT(d, DB_EVENT_REP_NEWMASTER); +#if (DBVER >= 46) + ADD_INT(d, DB_EVENT_REP_PERM_FAILED); +#endif + ADD_INT(d, DB_EVENT_REP_STARTUPDONE); + ADD_INT(d, DB_EVENT_WRITE_FAILED); +#endif + + ADD_INT(d, DB_REP_DUPMASTER); + ADD_INT(d, DB_REP_HOLDELECTION); +#if (DBVER >= 44) + ADD_INT(d, DB_REP_IGNORE); + ADD_INT(d, DB_REP_JOIN_FAILURE); +#endif +#if (DBVER >= 42) + ADD_INT(d, DB_REP_ISPERM); + ADD_INT(d, DB_REP_NOTPERM); +#endif + ADD_INT(d, DB_REP_NEWSITE); + + ADD_INT(d, DB_REP_MASTER); + ADD_INT(d, DB_REP_CLIENT); +#if (DBVER >= 45) + ADD_INT(d, DB_REP_ELECTION); + + ADD_INT(d, DB_REP_ACK_TIMEOUT); + ADD_INT(d, DB_REP_CONNECTION_RETRY); + ADD_INT(d, DB_REP_ELECTION_TIMEOUT); + ADD_INT(d, DB_REP_ELECTION_RETRY); +#endif +#if (DBVER >= 46) + ADD_INT(d, DB_REP_CHECKPOINT_DELAY); + ADD_INT(d, DB_REP_FULL_ELECTION_TIMEOUT); +#endif + +#if (DBVER >= 45) + ADD_INT(d, DB_REPMGR_PEER); + ADD_INT(d, DB_REPMGR_ACKS_ALL); + ADD_INT(d, DB_REPMGR_ACKS_ALL_PEERS); + ADD_INT(d, DB_REPMGR_ACKS_NONE); + ADD_INT(d, DB_REPMGR_ACKS_ONE); + ADD_INT(d, DB_REPMGR_ACKS_ONE_PEER); + ADD_INT(d, DB_REPMGR_ACKS_QUORUM); + ADD_INT(d, DB_REPMGR_CONNECTED); + ADD_INT(d, DB_REPMGR_DISCONNECTED); + ADD_INT(d, DB_STAT_CLEAR); + ADD_INT(d, DB_STAT_ALL); +#endif + +#if (DBVER >= 43) + ADD_INT(d, DB_BUFFER_SMALL); + ADD_INT(d, DB_SEQ_DEC); + ADD_INT(d, DB_SEQ_INC); + ADD_INT(d, DB_SEQ_WRAP); +#endif + +#if (DBVER >= 43) && (DBVER < 47) + ADD_INT(d, DB_LOG_INMEMORY); + ADD_INT(d, DB_DSYNC_LOG); +#endif + +#if (DBVER >= 41) + ADD_INT(d, DB_ENCRYPT_AES); + ADD_INT(d, DB_AUTO_COMMIT); +#else + /* allow Berkeley DB 4.1 aware apps to run on older versions */ + _addIntToDict(d, "DB_AUTO_COMMIT", 0); +#endif + + ADD_INT(d, EINVAL); + ADD_INT(d, EACCES); + ADD_INT(d, ENOSPC); + ADD_INT(d, ENOMEM); + ADD_INT(d, EAGAIN); + ADD_INT(d, EBUSY); + ADD_INT(d, EEXIST); + ADD_INT(d, ENOENT); + ADD_INT(d, EPERM); + + ADD_INT(d, DB_SET_LOCK_TIMEOUT); + ADD_INT(d, DB_SET_TXN_TIMEOUT); + + /* The exception name must be correct for pickled exception * + * objects to unpickle properly. */ +#ifdef PYBSDDB_STANDALONE /* different value needed for standalone pybsddb */ +#define PYBSDDB_EXCEPTION_BASE "bsddb3.db." +#else +#define PYBSDDB_EXCEPTION_BASE "bsddb.db." +#endif + + /* All the rest of the exceptions derive only from DBError */ +#define MAKE_EX(name) name = PyErr_NewException(PYBSDDB_EXCEPTION_BASE #name, DBError, NULL); \ + PyDict_SetItemString(d, #name, name) + + /* The base exception class is DBError */ + DBError = NULL; /* used in MAKE_EX so that it derives from nothing */ + MAKE_EX(DBError); + +#if (PY_VERSION_HEX < 0x03000000) + /* Some magic to make DBNotFoundError and DBKeyEmptyError derive + * from both DBError and KeyError, since the API only supports + * using one base class. */ + PyDict_SetItemString(d, "KeyError", PyExc_KeyError); + PyRun_String("class DBNotFoundError(DBError, KeyError): pass\n" + "class DBKeyEmptyError(DBError, KeyError): pass", + Py_file_input, d, d); + DBNotFoundError = PyDict_GetItemString(d, "DBNotFoundError"); + DBKeyEmptyError = PyDict_GetItemString(d, "DBKeyEmptyError"); + PyDict_DelItemString(d, "KeyError"); +#else + /* Since Python 2.5, PyErr_NewException() accepts a tuple, to be able to + ** derive from several classes. We use this new API only for Python 3.0, + ** though. + */ + { + PyObject* bases; + + bases = PyTuple_Pack(2, DBError, PyExc_KeyError); + +#define MAKE_EX2(name) name = PyErr_NewException(PYBSDDB_EXCEPTION_BASE #name, bases, NULL); \ + PyDict_SetItemString(d, #name, name) + MAKE_EX2(DBNotFoundError); + MAKE_EX2(DBKeyEmptyError); + +#undef MAKE_EX2 + + Py_XDECREF(bases); + } +#endif + + +#if !INCOMPLETE_IS_WARNING + MAKE_EX(DBIncompleteError); +#endif + MAKE_EX(DBCursorClosedError); + MAKE_EX(DBKeyEmptyError); + MAKE_EX(DBKeyExistError); + MAKE_EX(DBLockDeadlockError); + MAKE_EX(DBLockNotGrantedError); + MAKE_EX(DBOldVersionError); + MAKE_EX(DBRunRecoveryError); + MAKE_EX(DBVerifyBadError); + MAKE_EX(DBNoServerError); + MAKE_EX(DBNoServerHomeError); + MAKE_EX(DBNoServerIDError); + MAKE_EX(DBPageNotFoundError); + MAKE_EX(DBSecondaryBadError); + + MAKE_EX(DBInvalidArgError); + MAKE_EX(DBAccessError); + MAKE_EX(DBNoSpaceError); + MAKE_EX(DBNoMemoryError); + MAKE_EX(DBAgainError); + MAKE_EX(DBBusyError); + MAKE_EX(DBFileExistsError); + MAKE_EX(DBNoSuchFileError); + MAKE_EX(DBPermissionsError); + +#if (DBVER >= 42) + MAKE_EX(DBRepHandleDeadError); +#endif + + MAKE_EX(DBRepUnavailError); + +#undef MAKE_EX + + /* Initiliase the C API structure and add it to the module */ + bsddb_api.db_type = &DB_Type; + bsddb_api.dbcursor_type = &DBCursor_Type; + bsddb_api.dbenv_type = &DBEnv_Type; + bsddb_api.dbtxn_type = &DBTxn_Type; + bsddb_api.dblock_type = &DBLock_Type; +#if (DBVER >= 43) + bsddb_api.dbsequence_type = &DBSequence_Type; +#endif + bsddb_api.makeDBError = makeDBError; + + py_api = PyCObject_FromVoidPtr((void*)&bsddb_api, NULL); + PyDict_SetItemString(d, "api", py_api); + Py_DECREF(py_api); + + /* Check for errors */ + if (PyErr_Occurred()) { + PyErr_Print(); + Py_FatalError("can't initialize module _bsddb/_pybsddb"); + Py_DECREF(m); + m = NULL; + } +#if (PY_VERSION_HEX < 0x03000000) + return; +#else + return m; +#endif +} + +/* allow this module to be named _pybsddb so that it can be installed + * and imported on top of python >= 2.3 that includes its own older + * copy of the library named _bsddb without importing the old version. */ +#if (PY_VERSION_HEX < 0x03000000) +DL_EXPORT(void) init_pybsddb(void) +#else +PyMODINIT_FUNC PyInit__pybsddb(void) /* Note the two underscores */ +#endif +{ + strncpy(_bsddbModuleName, "_pybsddb", MODULE_NAME_MAX_LEN); +#if (PY_VERSION_HEX < 0x03000000) + init_bsddb(); +#else + return PyInit__bsddb(); /* Note the two underscores */ +#endif +} +