d00f3172f719073eeb9bb5b5816b9318680480cc
[samba.git] / lib / tdb / pytdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Python interface to tdb.
5
6    Copyright (C) 2004-2006 Tim Potter <tpot@samba.org>
7    Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
8
9      ** NOTE! The following LGPL license applies to the tdb
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 3 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include <Python.h>
28 #include "replace.h"
29 #include "system/filesys.h"
30
31 /* Include tdb headers */
32 #include <tdb.h>
33
34 #if PY_MAJOR_VERSION >= 3
35 #define PyInt_FromLong PyLong_FromLong
36 #define Py_TPFLAGS_HAVE_ITER 0
37 #endif
38
39 /* discard signature of 'func' in favour of 'target_sig' */
40 #define PY_DISCARD_FUNC_SIG(target_sig, func) (target_sig)(void(*)(void))func
41
42 typedef struct {
43         PyObject_HEAD
44         TDB_CONTEXT *ctx;
45         bool closed;
46 } PyTdbObject;
47
48 static PyTypeObject PyTdb;
49
50 static void PyErr_SetTDBError(TDB_CONTEXT *tdb)
51 {
52         PyErr_SetObject(PyExc_RuntimeError, 
53                 Py_BuildValue("(i,s)", tdb_error(tdb), tdb_errorstr(tdb)));
54 }
55
56 static TDB_DATA PyBytes_AsTDB_DATA(PyObject *data)
57 {
58         TDB_DATA ret;
59         ret.dptr = (unsigned char *)PyBytes_AsString(data);
60         ret.dsize = PyBytes_Size(data);
61         return ret;
62 }
63
64 static PyObject *PyBytes_FromTDB_DATA(TDB_DATA data)
65 {
66         if (data.dptr == NULL && data.dsize == 0) {
67                 Py_RETURN_NONE;
68         } else {
69                 PyObject *ret = PyBytes_FromStringAndSize((const char *)data.dptr,
70                                                                                                   data.dsize);
71                 free(data.dptr);
72                 return ret;
73     }
74 }
75
76 #define PyErr_TDB_ERROR_IS_ERR_RAISE(ret, tdb) \
77         if (ret != 0) { \
78                 PyErr_SetTDBError(tdb); \
79                 return NULL; \
80         }
81
82 #define PyErr_TDB_RAISE_IF_CLOSED(self) \
83         if (self->closed) {                                             \
84                 PyErr_SetObject(PyExc_RuntimeError,                             \
85                                 Py_BuildValue("(i,s)", TDB_ERR_IO, "Database is already closed")); \
86                 return NULL;                                            \
87         }
88
89 #define PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self) \
90         if (self->closed) {                                             \
91                 PyErr_SetObject(PyExc_RuntimeError,                             \
92                                 Py_BuildValue("(i,s)", TDB_ERR_IO, "Database is already closed")); \
93                 return -1;                                              \
94         }
95
96 static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
97 {
98         char *name = NULL;
99         int hash_size = 0, tdb_flags = TDB_DEFAULT, flags = O_RDWR, mode = 0600;
100         TDB_CONTEXT *ctx;
101         PyTdbObject *ret;
102         const char *_kwnames[] = { "name", "hash_size", "tdb_flags", "flags", "mode", NULL };
103         char **kwnames = discard_const_p(char *, _kwnames);
104
105         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii", kwnames, &name, &hash_size, &tdb_flags, &flags, &mode))
106                 return NULL;
107
108         if (name == NULL) {
109                 tdb_flags |= TDB_INTERNAL;
110         }
111
112         ctx = tdb_open(name, hash_size, tdb_flags, flags, mode);
113         if (ctx == NULL) {
114                 PyErr_SetFromErrno(PyExc_IOError);
115                 return NULL;
116         }
117
118         ret = PyObject_New(PyTdbObject, &PyTdb);
119         if (!ret) {
120                 tdb_close(ctx);
121                 return NULL;
122         }
123
124         ret->ctx = ctx;
125         ret->closed = false;
126         return (PyObject *)ret;
127 }
128
129 static PyObject *obj_transaction_cancel(PyTdbObject *self,
130                 PyObject *Py_UNUSED(ignored))
131 {
132         int ret;
133
134         PyErr_TDB_RAISE_IF_CLOSED(self);
135
136         ret = tdb_transaction_cancel(self->ctx);
137         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
138         Py_RETURN_NONE;
139 }
140
141 static PyObject *obj_transaction_commit(PyTdbObject *self,
142                 PyObject *Py_UNUSED(ignored))
143 {
144         int ret;
145         PyErr_TDB_RAISE_IF_CLOSED(self);
146         ret = tdb_transaction_commit(self->ctx);
147         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
148         Py_RETURN_NONE;
149 }
150
151 static PyObject *obj_transaction_prepare_commit(PyTdbObject *self,
152                 PyObject *Py_UNUSED(ignored))
153 {
154         int ret;
155         PyErr_TDB_RAISE_IF_CLOSED(self);
156         ret = tdb_transaction_prepare_commit(self->ctx);
157         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
158         Py_RETURN_NONE;
159 }
160
161 static PyObject *obj_transaction_start(PyTdbObject *self,
162                 PyObject *Py_UNUSED(ignored))
163 {
164         int ret;
165         PyErr_TDB_RAISE_IF_CLOSED(self);
166         ret = tdb_transaction_start(self->ctx);
167         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
168         Py_RETURN_NONE;
169 }
170
171 static PyObject *obj_reopen(PyTdbObject *self,
172                 PyObject *Py_UNUSED(ignored))
173 {
174         int ret;
175         PyErr_TDB_RAISE_IF_CLOSED(self);
176         ret = tdb_reopen(self->ctx);
177         if (ret != 0) {
178                 self->closed = true;
179                 PyErr_SetObject(PyExc_RuntimeError,
180                                 Py_BuildValue("(i,s)",
181                                               TDB_ERR_IO,
182                                               "Failed to reopen database"));
183                 return NULL;
184         }
185         Py_RETURN_NONE;
186 }
187
188 static PyObject *obj_lockall(PyTdbObject *self,
189                 PyObject *Py_UNUSED(ignored))
190 {
191         int ret;
192         PyErr_TDB_RAISE_IF_CLOSED(self);
193         ret = tdb_lockall(self->ctx);
194         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
195         Py_RETURN_NONE;
196 }
197
198 static PyObject *obj_unlockall(PyTdbObject *self,
199                 PyObject *Py_UNUSED(ignored))
200 {
201         int ret;
202         PyErr_TDB_RAISE_IF_CLOSED(self);
203         ret = tdb_unlockall(self->ctx);
204         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
205         Py_RETURN_NONE;
206 }
207
208 static PyObject *obj_lockall_read(PyTdbObject *self,
209                 PyObject *Py_UNUSED(ignored))
210 {
211         int ret;
212         PyErr_TDB_RAISE_IF_CLOSED(self);
213         ret = tdb_lockall_read(self->ctx);
214         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
215         Py_RETURN_NONE;
216 }
217
218 static PyObject *obj_unlockall_read(PyTdbObject *self,
219                 PyObject *Py_UNUSED(ignored))
220 {
221         int ret = tdb_unlockall_read(self->ctx);
222         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
223         Py_RETURN_NONE;
224 }
225
226 static PyObject *obj_close(PyTdbObject *self, PyObject *Py_UNUSED(ignored))
227 {
228         int ret;
229         if (self->closed)
230                 Py_RETURN_NONE;
231         ret = tdb_close(self->ctx);
232         self->closed = true;
233         if (ret != 0) {
234                 PyErr_SetObject(PyExc_RuntimeError,
235                                 Py_BuildValue("(i,s)",
236                                               TDB_ERR_IO,
237                                               "Failed to close database"));
238                 return NULL;
239         }
240         Py_RETURN_NONE;
241 }
242
243 static PyObject *obj_get(PyTdbObject *self, PyObject *args)
244 {
245         TDB_DATA key;
246         PyObject *py_key;
247
248         PyErr_TDB_RAISE_IF_CLOSED(self);
249
250         if (!PyArg_ParseTuple(args, "O", &py_key))
251                 return NULL;
252
253         key = PyBytes_AsTDB_DATA(py_key);
254         if (!key.dptr)
255                 return NULL;
256
257         return PyBytes_FromTDB_DATA(tdb_fetch(self->ctx, key));
258 }
259
260 static PyObject *obj_append(PyTdbObject *self, PyObject *args)
261 {
262         TDB_DATA key, data;
263         PyObject *py_key, *py_data;
264         int ret;
265
266         PyErr_TDB_RAISE_IF_CLOSED(self);
267
268         if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data))
269                 return NULL;
270
271         key = PyBytes_AsTDB_DATA(py_key);
272         if (!key.dptr)
273                 return NULL;
274         data = PyBytes_AsTDB_DATA(py_data);
275         if (!data.dptr)
276                 return NULL;
277
278         ret = tdb_append(self->ctx, key, data);
279         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
280         Py_RETURN_NONE;
281 }
282
283 static PyObject *obj_firstkey(PyTdbObject *self, PyObject *Py_UNUSED(ignored))
284 {
285         PyErr_TDB_RAISE_IF_CLOSED(self);
286
287         return PyBytes_FromTDB_DATA(tdb_firstkey(self->ctx));
288 }
289
290 static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args)
291 {
292         TDB_DATA key;
293         PyObject *py_key;
294         PyErr_TDB_RAISE_IF_CLOSED(self);
295
296         if (!PyArg_ParseTuple(args, "O", &py_key))
297                 return NULL;
298
299         key = PyBytes_AsTDB_DATA(py_key);
300         if (!key.dptr)
301                 return NULL;
302         
303         return PyBytes_FromTDB_DATA(tdb_nextkey(self->ctx, key));
304 }
305
306 static PyObject *obj_delete(PyTdbObject *self, PyObject *args)
307 {
308         TDB_DATA key;
309         PyObject *py_key;
310         int ret;
311         PyErr_TDB_RAISE_IF_CLOSED(self);
312
313         if (!PyArg_ParseTuple(args, "O", &py_key))
314                 return NULL;
315
316         key = PyBytes_AsTDB_DATA(py_key);
317         if (!key.dptr)
318                 return NULL;
319         ret = tdb_delete(self->ctx, key);
320         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
321         Py_RETURN_NONE;
322 }
323
324 static int obj_contains(PyTdbObject *self, PyObject *py_key)
325 {
326         TDB_DATA key;
327         int ret;
328         PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self);
329
330         key = PyBytes_AsTDB_DATA(py_key);
331         if (!key.dptr) {
332                 PyErr_BadArgument();
333                 return -1;
334         }
335         ret = tdb_exists(self->ctx, key);
336         if (ret)
337                 return 1;
338         return 0;
339 }
340
341 #if PY_MAJOR_VERSION < 3
342 static PyObject *obj_has_key(PyTdbObject *self, PyObject *args)
343 {
344         int ret;
345         PyObject *py_key;
346         PyErr_TDB_RAISE_IF_CLOSED(self);
347
348         if (!PyArg_ParseTuple(args, "O", &py_key))
349                 return NULL;
350
351         ret = obj_contains(self, py_key);
352         if (ret == -1)
353                 return NULL;
354         if (ret)
355                 Py_RETURN_TRUE;
356         Py_RETURN_FALSE;
357
358 }
359 #endif
360
361 static PyObject *obj_store(PyTdbObject *self, PyObject *args)
362 {
363         TDB_DATA key, value;
364         int ret;
365         int flag = TDB_REPLACE;
366         PyObject *py_key, *py_value;
367
368         PyErr_TDB_RAISE_IF_CLOSED(self);
369
370         if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag))
371                 return NULL;
372
373         key = PyBytes_AsTDB_DATA(py_key);
374         if (!key.dptr)
375                 return NULL;
376         value = PyBytes_AsTDB_DATA(py_value);
377         if (!value.dptr)
378                 return NULL;
379
380         ret = tdb_store(self->ctx, key, value, flag);
381         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
382         Py_RETURN_NONE;
383 }
384
385 static PyObject *obj_add_flags(PyTdbObject *self, PyObject *args)
386 {
387         unsigned flags;
388
389         PyErr_TDB_RAISE_IF_CLOSED(self);
390
391         if (!PyArg_ParseTuple(args, "I", &flags))
392                 return NULL;
393
394         tdb_add_flags(self->ctx, flags);
395         Py_RETURN_NONE;
396 }
397
398 static PyObject *obj_remove_flags(PyTdbObject *self, PyObject *args)
399 {
400         unsigned flags;
401
402         PyErr_TDB_RAISE_IF_CLOSED(self);
403
404         if (!PyArg_ParseTuple(args, "I", &flags))
405                 return NULL;
406
407         tdb_remove_flags(self->ctx, flags);
408         Py_RETURN_NONE;
409 }
410
411 typedef struct {
412         PyObject_HEAD
413         TDB_DATA current;
414         PyTdbObject *iteratee;
415 } PyTdbIteratorObject;
416
417 static PyObject *tdb_iter_next(PyTdbIteratorObject *self)
418 {
419         TDB_DATA current;
420         PyObject *ret;
421         if (self->current.dptr == NULL && self->current.dsize == 0)
422                 return NULL;
423         current = self->current;
424         self->current = tdb_nextkey(self->iteratee->ctx, self->current);
425         ret = PyBytes_FromTDB_DATA(current);
426         return ret;
427 }
428
429 static void tdb_iter_dealloc(PyTdbIteratorObject *self)
430 {
431         Py_DECREF(self->iteratee);
432         PyObject_Del(self);
433 }
434
435 PyTypeObject PyTdbIterator = {
436         .tp_name = "Iterator",
437         .tp_basicsize = sizeof(PyTdbIteratorObject),
438         .tp_iternext = (iternextfunc)tdb_iter_next,
439         .tp_dealloc = (destructor)tdb_iter_dealloc,
440         .tp_flags = Py_TPFLAGS_DEFAULT,
441         .tp_iter = PyObject_SelfIter,
442 };
443
444 static PyObject *tdb_object_iter(PyTdbObject *self,
445                 PyObject *Py_UNUSED(ignored))
446 {
447         PyTdbIteratorObject *ret;       
448
449         PyErr_TDB_RAISE_IF_CLOSED(self);
450
451         ret = PyObject_New(PyTdbIteratorObject, &PyTdbIterator);
452         if (!ret)
453                 return NULL;
454         ret->current = tdb_firstkey(self->ctx);
455         ret->iteratee = self;
456         Py_INCREF(self);
457         return (PyObject *)ret;
458 }
459
460 static PyObject *obj_clear(PyTdbObject *self, PyObject *Py_UNUSED(ignored))
461 {
462         int ret;
463         PyErr_TDB_RAISE_IF_CLOSED(self);
464         ret = tdb_wipe_all(self->ctx);
465         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
466         Py_RETURN_NONE;
467 }
468
469 static PyObject *obj_repack(PyTdbObject *self, PyObject *Py_UNUSED(ignored))
470 {
471         int ret;
472         PyErr_TDB_RAISE_IF_CLOSED(self);
473         ret = tdb_repack(self->ctx);
474         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
475         Py_RETURN_NONE;
476 }
477
478 static PyObject *obj_enable_seqnum(PyTdbObject *self,
479                 PyObject *Py_UNUSED(ignored))
480 {
481         PyErr_TDB_RAISE_IF_CLOSED(self);
482         tdb_enable_seqnum(self->ctx);
483         Py_RETURN_NONE;
484 }
485
486 static PyObject *obj_increment_seqnum_nonblock(PyTdbObject *self,
487                 PyObject *Py_UNUSED(ignored))
488 {
489         PyErr_TDB_RAISE_IF_CLOSED(self);
490         tdb_increment_seqnum_nonblock(self->ctx);
491         Py_RETURN_NONE;
492 }
493
494 static PyMethodDef tdb_object_methods[] = {
495         { "transaction_cancel", (PyCFunction)obj_transaction_cancel, METH_NOARGS, 
496                 "S.transaction_cancel() -> None\n"
497                 "Cancel the currently active transaction." },
498         { "transaction_commit", (PyCFunction)obj_transaction_commit, METH_NOARGS,
499                 "S.transaction_commit() -> None\n"
500                 "Commit the currently active transaction." },
501         { "transaction_prepare_commit", (PyCFunction)obj_transaction_prepare_commit, METH_NOARGS,
502                 "S.transaction_prepare_commit() -> None\n"
503                 "Prepare to commit the currently active transaction" },
504         { "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS,
505                 "S.transaction_start() -> None\n"
506                 "Start a new transaction." },
507         { "reopen", (PyCFunction)obj_reopen, METH_NOARGS, "Reopen this file." },
508         { "lock_all", (PyCFunction)obj_lockall, METH_NOARGS, NULL },
509         { "unlock_all", (PyCFunction)obj_unlockall, METH_NOARGS, NULL },
510         { "read_lock_all", (PyCFunction)obj_lockall_read, METH_NOARGS, NULL },
511         { "read_unlock_all", (PyCFunction)obj_unlockall_read, METH_NOARGS, NULL },
512         { "close", (PyCFunction)obj_close, METH_NOARGS, NULL },
513         { "get", (PyCFunction)obj_get, METH_VARARGS, "S.get(key) -> value\n"
514                 "Fetch a value." },
515         { "append", (PyCFunction)obj_append, METH_VARARGS, "S.append(key, value) -> None\n"
516                 "Append data to an existing key." },
517         { "firstkey", (PyCFunction)obj_firstkey, METH_NOARGS, "S.firstkey() -> data\n"
518                 "Return the first key in this database." },
519         { "nextkey", (PyCFunction)obj_nextkey, METH_VARARGS, "S.nextkey(key) -> data\n"
520                 "Return the next key in this database." },
521         { "delete", (PyCFunction)obj_delete, METH_VARARGS, "S.delete(key) -> None\n"
522                 "Delete an entry." },
523 #if PY_MAJOR_VERSION < 3
524         { "has_key", (PyCFunction)obj_has_key, METH_VARARGS, "S.has_key(key) -> None\n"
525                 "Check whether key exists in this database." },
526 #endif
527         { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None"
528                 "Store data." },
529         { "add_flags", (PyCFunction)obj_add_flags, METH_VARARGS, "S.add_flags(flags) -> None" },
530         { "remove_flags", (PyCFunction)obj_remove_flags, METH_VARARGS, "S.remove_flags(flags) -> None" },
531 #if PY_MAJOR_VERSION >= 3
532         { "keys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" },
533 #else
534         { "iterkeys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" },
535 #endif
536         { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n"
537                 "Wipe the entire database." },
538         { "repack", (PyCFunction)obj_repack, METH_NOARGS, "S.repack() -> None\n"
539                 "Repack the entire database." },
540         { "enable_seqnum", (PyCFunction)obj_enable_seqnum, METH_NOARGS,
541                 "S.enable_seqnum() -> None" },
542         { "increment_seqnum_nonblock", (PyCFunction)obj_increment_seqnum_nonblock, METH_NOARGS,
543                 "S.increment_seqnum_nonblock() -> None" },
544         { NULL }
545 };
546
547 static PyObject *obj_get_hash_size(PyTdbObject *self, void *closure)
548 {
549         PyErr_TDB_RAISE_IF_CLOSED(self);
550         return PyInt_FromLong(tdb_hash_size(self->ctx));
551 }
552
553 static int obj_set_max_dead(PyTdbObject *self, PyObject *max_dead, void *closure)
554 {
555         PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self);
556         if (!PyLong_Check(max_dead))
557                 return -1;
558         tdb_set_max_dead(self->ctx, PyLong_AsLong(max_dead));
559         return 0;
560 }
561
562 static PyObject *obj_get_map_size(PyTdbObject *self, void *closure)
563 {
564         PyErr_TDB_RAISE_IF_CLOSED(self);
565         return PyInt_FromLong(tdb_map_size(self->ctx));
566 }
567
568 static PyObject *obj_get_freelist_size(PyTdbObject *self, void *closure)
569 {
570         PyErr_TDB_RAISE_IF_CLOSED(self);
571         return PyInt_FromLong(tdb_freelist_size(self->ctx));
572 }
573
574 static PyObject *obj_get_flags(PyTdbObject *self, void *closure)
575 {
576         PyErr_TDB_RAISE_IF_CLOSED(self);
577         return PyInt_FromLong(tdb_get_flags(self->ctx));
578 }
579
580 static PyObject *obj_get_filename(PyTdbObject *self, void *closure)
581 {
582         PyErr_TDB_RAISE_IF_CLOSED(self);
583         return PyBytes_FromString(tdb_name(self->ctx));
584 }
585
586 static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure)
587 {
588         PyErr_TDB_RAISE_IF_CLOSED(self);
589         return PyInt_FromLong(tdb_get_seqnum(self->ctx));
590 }
591
592 static PyObject *obj_get_text(PyTdbObject *self, void *closure)
593 {
594         PyObject *mod, *cls, *inst;
595         mod = PyImport_ImportModule("_tdb_text");
596         if (mod == NULL)
597                 return NULL;
598         cls = PyObject_GetAttrString(mod, "TdbTextWrapper");
599         if (cls == NULL) {
600                 Py_DECREF(mod);
601                 return NULL;
602         }
603         inst = PyObject_CallFunction(cls, discard_const_p(char, "O"), self);
604         Py_DECREF(mod);
605         Py_DECREF(cls);
606         return inst;
607 }
608
609 static PyGetSetDef tdb_object_getsetters[] = {
610         {
611                 .name    = discard_const_p(char, "hash_size"),
612                 .get     = (getter)obj_get_hash_size,
613         },
614         {
615                 .name    = discard_const_p(char, "map_size"),
616                 .get     = (getter)obj_get_map_size,
617         },
618         {
619                 .name    = discard_const_p(char, "freelist_size"),
620                 .get     = (getter)obj_get_freelist_size,
621         },
622         {
623                 .name    = discard_const_p(char, "flags"),
624                 .get     = (getter)obj_get_flags,
625         },
626         {
627                 .name    = discard_const_p(char, "max_dead"),
628                 .set     = (setter)obj_set_max_dead,
629         },
630         {
631                 .name    = discard_const_p(char, "filename"),
632                 .get     = (getter)obj_get_filename,
633                 .doc     = discard_const_p(char, "The filename of this TDB file."),
634         },
635         {
636                 .name    = discard_const_p(char, "seqnum"),
637                 .get     = (getter)obj_get_seqnum,
638         },
639         {
640                 .name    = discard_const_p(char, "text"),
641                 .get     = (getter)obj_get_text,
642         },
643         { .name = NULL }
644 };
645
646 static PyObject *tdb_object_repr(PyTdbObject *self)
647 {
648         PyErr_TDB_RAISE_IF_CLOSED(self);
649         if (tdb_get_flags(self->ctx) & TDB_INTERNAL) {
650                 return PyUnicode_FromString("Tdb(<internal>)");
651         } else {
652                 return PyUnicode_FromFormat("Tdb('%s')", tdb_name(self->ctx));
653         }
654 }
655
656 static void tdb_object_dealloc(PyTdbObject *self)
657 {
658         if (!self->closed)
659                 tdb_close(self->ctx);
660         Py_TYPE(self)->tp_free(self);
661 }
662
663 static PyObject *obj_getitem(PyTdbObject *self, PyObject *key)
664 {
665         TDB_DATA tkey, val;
666         PyErr_TDB_RAISE_IF_CLOSED(self);
667         if (!PyBytes_Check(key)) {
668                 PyErr_SetString(PyExc_TypeError, "Expected bytestring as key");
669                 return NULL;
670         }
671
672         tkey.dptr = (unsigned char *)PyBytes_AsString(key);
673         tkey.dsize = PyBytes_Size(key);
674
675         val = tdb_fetch(self->ctx, tkey);
676         if (val.dptr == NULL) {
677                 /*
678                  * if the key doesn't exist raise KeyError(key) to be
679                  * consistent with python dict
680                  */
681                 PyErr_SetObject(PyExc_KeyError, key);
682                 return NULL;
683         } else {
684                 return PyBytes_FromTDB_DATA(val);
685         }
686 }
687
688 static int obj_setitem(PyTdbObject *self, PyObject *key, PyObject *value)
689 {
690         TDB_DATA tkey, tval;
691         int ret;
692         PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self);
693         if (!PyBytes_Check(key)) {
694                 PyErr_SetString(PyExc_TypeError, "Expected bytestring as key");
695                 return -1;
696         }
697
698         tkey = PyBytes_AsTDB_DATA(key);
699
700         if (value == NULL) { 
701                 ret = tdb_delete(self->ctx, tkey);
702         } else { 
703                 if (!PyBytes_Check(value)) {
704                         PyErr_SetString(PyExc_TypeError, "Expected string as value");
705                         return -1;
706                 }
707
708                 tval = PyBytes_AsTDB_DATA(value);
709
710                 ret = tdb_store(self->ctx, tkey, tval, TDB_REPLACE);
711         }
712
713         if (ret != 0) {
714                 PyErr_SetTDBError(self->ctx);
715                 return -1;
716         } 
717
718         return ret;
719 }
720
721 static PyMappingMethods tdb_object_mapping = {
722         .mp_subscript = (binaryfunc)obj_getitem,
723         .mp_ass_subscript = (objobjargproc)obj_setitem,
724 };
725 static PySequenceMethods tdb_object_seq = {
726         .sq_contains = (objobjproc)obj_contains,
727 };
728 static PyTypeObject PyTdb = {
729         .tp_name = "tdb.Tdb",
730         .tp_basicsize = sizeof(PyTdbObject),
731         .tp_methods = tdb_object_methods,
732         .tp_getset = tdb_object_getsetters,
733         .tp_new = py_tdb_open,
734         .tp_doc = "A TDB file",
735         .tp_repr = (reprfunc)tdb_object_repr,
736         .tp_dealloc = (destructor)tdb_object_dealloc,
737         .tp_as_mapping = &tdb_object_mapping,
738         .tp_as_sequence = &tdb_object_seq,
739         .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER,
740         .tp_iter = PY_DISCARD_FUNC_SIG(getiterfunc,tdb_object_iter),
741 };
742
743 static PyMethodDef tdb_methods[] = {
744         {
745                 .ml_name  = "open",
746                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction, py_tdb_open),
747                 .ml_flags = METH_VARARGS|METH_KEYWORDS,
748                 .ml_doc   = "open(name, hash_size=0, tdb_flags=TDB_DEFAULT, "
749                             "flags=O_RDWR, mode=0600)\nOpen a TDB file."
750         },
751         { .ml_name = NULL }
752 };
753
754 #define MODULE_DOC "simple key-value database that supports multiple writers."
755
756 #if PY_MAJOR_VERSION >= 3
757 static struct PyModuleDef moduledef = {
758     PyModuleDef_HEAD_INIT,
759     .m_name = "tdb",
760     .m_doc = MODULE_DOC,
761     .m_size = -1,
762     .m_methods = tdb_methods,
763 };
764 #endif
765
766 PyObject* module_init(void);
767 PyObject* module_init(void)
768 {
769         PyObject *m;
770
771         if (PyType_Ready(&PyTdb) < 0)
772                 return NULL;
773
774         if (PyType_Ready(&PyTdbIterator) < 0)
775                 return NULL;
776
777 #if PY_MAJOR_VERSION >= 3
778         m = PyModule_Create(&moduledef);
779 #else
780         m = Py_InitModule3("tdb", tdb_methods, MODULE_DOC);
781 #endif
782         if (m == NULL)
783                 return NULL;
784
785         PyModule_AddIntConstant(m, "REPLACE", TDB_REPLACE);
786         PyModule_AddIntConstant(m, "INSERT", TDB_INSERT);
787         PyModule_AddIntConstant(m, "MODIFY", TDB_MODIFY);
788
789         PyModule_AddIntConstant(m, "DEFAULT", TDB_DEFAULT);
790         PyModule_AddIntConstant(m, "CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST);
791         PyModule_AddIntConstant(m, "INTERNAL", TDB_INTERNAL);
792         PyModule_AddIntConstant(m, "NOLOCK", TDB_NOLOCK);
793         PyModule_AddIntConstant(m, "NOMMAP", TDB_NOMMAP);
794         PyModule_AddIntConstant(m, "CONVERT", TDB_CONVERT);
795         PyModule_AddIntConstant(m, "BIGENDIAN", TDB_BIGENDIAN);
796         PyModule_AddIntConstant(m, "NOSYNC", TDB_NOSYNC);
797         PyModule_AddIntConstant(m, "SEQNUM", TDB_SEQNUM);
798         PyModule_AddIntConstant(m, "VOLATILE", TDB_VOLATILE);
799         PyModule_AddIntConstant(m, "ALLOW_NESTING", TDB_ALLOW_NESTING);
800         PyModule_AddIntConstant(m, "DISALLOW_NESTING", TDB_DISALLOW_NESTING);
801         PyModule_AddIntConstant(m, "INCOMPATIBLE_HASH", TDB_INCOMPATIBLE_HASH);
802
803         PyModule_AddStringConstant(m, "__docformat__", "restructuredText");
804
805         PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
806
807         Py_INCREF(&PyTdb);
808         PyModule_AddObject(m, "Tdb", (PyObject *)&PyTdb);
809
810         Py_INCREF(&PyTdbIterator);
811
812     return m;
813 }
814
815
816 #if PY_MAJOR_VERSION >= 3
817 PyMODINIT_FUNC PyInit_tdb(void);
818 PyMODINIT_FUNC PyInit_tdb(void)
819 {
820     return module_init();
821 }
822 #else
823 void inittdb(void);
824 void inittdb(void)
825 {
826     module_init();
827 }
828 #endif