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