c2ba661369233a4e297d67bf3051da4b573a186b
[metze/ctdb/wip.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 "replace.h"
28 #include "system/filesys.h"
29
30 #include <Python.h>
31 #ifndef Py_RETURN_NONE
32 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
33 #endif
34
35 /* Include tdb headers */
36 #include <tdb.h>
37
38 typedef struct {
39         PyObject_HEAD
40         TDB_CONTEXT *ctx;
41         bool closed;
42 } PyTdbObject;
43
44 PyAPI_DATA(PyTypeObject) PyTdb;
45
46 static void PyErr_SetTDBError(TDB_CONTEXT *tdb)
47 {
48         PyErr_SetObject(PyExc_RuntimeError, 
49                 Py_BuildValue("(i,s)", tdb_error(tdb), tdb_errorstr(tdb)));
50 }
51
52 static TDB_DATA PyString_AsTDB_DATA(PyObject *data)
53 {
54         TDB_DATA ret;
55         ret.dptr = (unsigned char *)PyString_AsString(data);
56         ret.dsize = PyString_Size(data);
57         return ret;
58 }
59
60 static PyObject *PyString_FromTDB_DATA(TDB_DATA data)
61 {
62         if (data.dptr == NULL && data.dsize == 0) {
63                 Py_RETURN_NONE;
64         } else {
65                 PyObject *ret = PyString_FromStringAndSize((const char *)data.dptr, 
66                                                                                                    data.dsize);
67                 free(data.dptr);
68                 return ret;
69     }
70 }
71
72 #define PyErr_TDB_ERROR_IS_ERR_RAISE(ret, tdb) \
73         if (ret != 0) { \
74                 PyErr_SetTDBError(tdb); \
75                 return NULL; \
76         }
77
78 static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
79 {
80         char *name = NULL;
81         int hash_size = 0, tdb_flags = TDB_DEFAULT, flags = O_RDWR, mode = 0600;
82         TDB_CONTEXT *ctx;
83         PyTdbObject *ret;
84         const char *kwnames[] = { "name", "hash_size", "tdb_flags", "flags", "mode", NULL };
85
86         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode))
87                 return NULL;
88
89         if (name == NULL) {
90                 tdb_flags |= TDB_INTERNAL;
91         }
92
93         ctx = tdb_open(name, hash_size, tdb_flags, flags, mode);
94         if (ctx == NULL) {
95                 PyErr_SetFromErrno(PyExc_IOError);
96                 return NULL;
97         }
98
99         ret = PyObject_New(PyTdbObject, &PyTdb);
100         ret->ctx = ctx;
101         ret->closed = false;
102         return (PyObject *)ret;
103 }
104
105 static PyObject *obj_transaction_cancel(PyTdbObject *self)
106 {
107         int ret = tdb_transaction_cancel(self->ctx);
108         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
109         Py_RETURN_NONE;
110 }
111
112 static PyObject *obj_transaction_commit(PyTdbObject *self)
113 {
114         int ret = tdb_transaction_commit(self->ctx);
115         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
116         Py_RETURN_NONE;
117 }
118
119 static PyObject *obj_transaction_prepare_commit(PyTdbObject *self)
120 {
121         int ret = tdb_transaction_prepare_commit(self->ctx);
122         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
123         Py_RETURN_NONE;
124 }
125
126 static PyObject *obj_transaction_start(PyTdbObject *self)
127 {
128         int ret = tdb_transaction_start(self->ctx);
129         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
130         Py_RETURN_NONE;
131 }
132
133 static PyObject *obj_reopen(PyTdbObject *self)
134 {
135         int ret = tdb_reopen(self->ctx);
136         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
137         Py_RETURN_NONE;
138 }
139
140 static PyObject *obj_lockall(PyTdbObject *self)
141 {
142         int ret = tdb_lockall(self->ctx);
143         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
144         Py_RETURN_NONE;
145 }
146
147 static PyObject *obj_unlockall(PyTdbObject *self)
148 {
149         int ret = tdb_unlockall(self->ctx);
150         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
151         Py_RETURN_NONE;
152 }
153
154 static PyObject *obj_lockall_read(PyTdbObject *self)
155 {
156         int ret = tdb_lockall_read(self->ctx);
157         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
158         Py_RETURN_NONE;
159 }
160
161 static PyObject *obj_unlockall_read(PyTdbObject *self)
162 {
163         int ret = tdb_unlockall_read(self->ctx);
164         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
165         Py_RETURN_NONE;
166 }
167
168 static PyObject *obj_close(PyTdbObject *self)
169 {
170         int ret;
171         if (self->closed)
172                 Py_RETURN_NONE;
173         ret = tdb_close(self->ctx);
174         self->closed = true;
175         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
176         Py_RETURN_NONE;
177 }
178
179 static PyObject *obj_get(PyTdbObject *self, PyObject *args)
180 {
181         TDB_DATA key;
182         PyObject *py_key;
183         if (!PyArg_ParseTuple(args, "O", &py_key))
184                 return NULL;
185
186         key = PyString_AsTDB_DATA(py_key);
187
188         return PyString_FromTDB_DATA(tdb_fetch(self->ctx, key));
189 }
190
191 static PyObject *obj_append(PyTdbObject *self, PyObject *args)
192 {
193         TDB_DATA key, data;
194         PyObject *py_key, *py_data;
195         int ret;
196         if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data))
197                 return NULL;
198
199         key = PyString_AsTDB_DATA(py_key);
200         data = PyString_AsTDB_DATA(py_data);
201
202         ret = tdb_append(self->ctx, key, data);
203         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
204         Py_RETURN_NONE;
205 }
206
207 static PyObject *obj_firstkey(PyTdbObject *self)
208 {
209         return PyString_FromTDB_DATA(tdb_firstkey(self->ctx));
210 }
211
212 static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args)
213 {
214         TDB_DATA key;
215         PyObject *py_key;
216         if (!PyArg_ParseTuple(args, "O", &py_key))
217                 return NULL;
218
219         key = PyString_AsTDB_DATA(py_key);
220         
221         return PyString_FromTDB_DATA(tdb_nextkey(self->ctx, key));
222 }
223
224 static PyObject *obj_delete(PyTdbObject *self, PyObject *args)
225 {
226         TDB_DATA key;
227         PyObject *py_key;
228         int ret;
229         if (!PyArg_ParseTuple(args, "O", &py_key))
230                 return NULL;
231
232         key = PyString_AsTDB_DATA(py_key);
233         ret = tdb_delete(self->ctx, key);
234         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
235         Py_RETURN_NONE;
236 }
237
238 static PyObject *obj_has_key(PyTdbObject *self, PyObject *args)
239 {
240         TDB_DATA key;
241         int ret;
242         PyObject *py_key;
243         if (!PyArg_ParseTuple(args, "O", &py_key))
244                 return NULL;
245
246         key = PyString_AsTDB_DATA(py_key);
247         ret = tdb_exists(self->ctx, key);
248         if (ret != TDB_ERR_NOEXIST) {
249                 PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
250         }
251
252         return (ret == TDB_ERR_NOEXIST)?Py_False:Py_True;
253 }
254
255 static PyObject *obj_store(PyTdbObject *self, PyObject *args)
256 {
257         TDB_DATA key, value;
258         int ret;
259         int flag = TDB_REPLACE;
260         PyObject *py_key, *py_value;
261
262         if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag))
263                 return NULL;
264
265         key = PyString_AsTDB_DATA(py_key);
266         value = PyString_AsTDB_DATA(py_value);
267
268         ret = tdb_store(self->ctx, key, value, flag);
269         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
270         Py_RETURN_NONE;
271 }
272
273 static PyObject *obj_add_flags(PyTdbObject *self, PyObject *args)
274 {
275         unsigned flags;
276
277         if (!PyArg_ParseTuple(args, "I", &flags))
278                 return NULL;
279
280         tdb_add_flags(self->ctx, flags);
281         Py_RETURN_NONE;
282 }
283
284 static PyObject *obj_remove_flags(PyTdbObject *self, PyObject *args)
285 {
286         unsigned flags;
287
288         if (!PyArg_ParseTuple(args, "I", &flags))
289                 return NULL;
290
291         tdb_remove_flags(self->ctx, flags);
292         Py_RETURN_NONE;
293 }
294
295 typedef struct {
296         PyObject_HEAD
297         TDB_DATA current;
298         PyTdbObject *iteratee;
299 } PyTdbIteratorObject;
300
301 static PyObject *tdb_iter_next(PyTdbIteratorObject *self)
302 {
303         TDB_DATA current;
304         PyObject *ret;
305         if (self->current.dptr == NULL && self->current.dsize == 0)
306                 return NULL;
307         current = self->current;
308         self->current = tdb_nextkey(self->iteratee->ctx, self->current);
309         ret = PyString_FromTDB_DATA(current);
310         return ret;
311 }
312
313 static void tdb_iter_dealloc(PyTdbIteratorObject *self)
314 {
315         Py_DECREF(self->iteratee);
316         PyObject_Del(self);
317 }
318
319 PyTypeObject PyTdbIterator = {
320         .tp_name = "Iterator",
321         .tp_basicsize = sizeof(PyTdbIteratorObject),
322         .tp_iternext = (iternextfunc)tdb_iter_next,
323         .tp_dealloc = (destructor)tdb_iter_dealloc,
324         .tp_flags = Py_TPFLAGS_DEFAULT,
325         .tp_iter = PyObject_SelfIter,
326 };
327
328 static PyObject *tdb_object_iter(PyTdbObject *self)
329 {
330         PyTdbIteratorObject *ret;       
331
332         ret = PyObject_New(PyTdbIteratorObject, &PyTdbIterator);
333         ret->current = tdb_firstkey(self->ctx);
334         ret->iteratee = self;
335         Py_INCREF(self);
336         return (PyObject *)ret;
337 }
338
339 static PyObject *obj_clear(PyTdbObject *self)
340 {
341         int ret = tdb_wipe_all(self->ctx);
342         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
343         Py_RETURN_NONE;
344 }
345
346 static PyObject *obj_enable_seqnum(PyTdbObject *self)
347 {
348         tdb_enable_seqnum(self->ctx);
349         Py_RETURN_NONE;
350 }
351
352 static PyObject *obj_increment_seqnum_nonblock(PyTdbObject *self)
353 {
354         tdb_increment_seqnum_nonblock(self->ctx);
355         Py_RETURN_NONE;
356 }
357
358 static PyMethodDef tdb_object_methods[] = {
359         { "transaction_cancel", (PyCFunction)obj_transaction_cancel, METH_NOARGS, 
360                 "S.transaction_cancel() -> None\n"
361                 "Cancel the currently active transaction." },
362         { "transaction_commit", (PyCFunction)obj_transaction_commit, METH_NOARGS,
363                 "S.transaction_commit() -> None\n"
364                 "Commit the currently active transaction." },
365         { "transaction_prepare_commit", (PyCFunction)obj_transaction_prepare_commit, METH_NOARGS,
366                 "S.transaction_prepare_commit() -> None\n"
367                 "Prepare to commit the currently active transaction" },
368         { "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS,
369                 "S.transaction_start() -> None\n"
370                 "Start a new transaction." },
371         { "reopen", (PyCFunction)obj_reopen, METH_NOARGS, "Reopen this file." },
372         { "lock_all", (PyCFunction)obj_lockall, METH_NOARGS, NULL },
373         { "unlock_all", (PyCFunction)obj_unlockall, METH_NOARGS, NULL },
374         { "read_lock_all", (PyCFunction)obj_lockall_read, METH_NOARGS, NULL },
375         { "read_unlock_all", (PyCFunction)obj_unlockall_read, METH_NOARGS, NULL },
376         { "close", (PyCFunction)obj_close, METH_NOARGS, NULL },
377         { "get", (PyCFunction)obj_get, METH_VARARGS, "S.get(key) -> value\n"
378                 "Fetch a value." },
379         { "append", (PyCFunction)obj_append, METH_VARARGS, "S.append(key, value) -> None\n"
380                 "Append data to an existing key." },
381         { "firstkey", (PyCFunction)obj_firstkey, METH_NOARGS, "S.firstkey() -> data\n"
382                 "Return the first key in this database." },
383         { "nextkey", (PyCFunction)obj_nextkey, METH_NOARGS, "S.nextkey(key) -> data\n"
384                 "Return the next key in this database." },
385         { "delete", (PyCFunction)obj_delete, METH_VARARGS, "S.delete(key) -> None\n"
386                 "Delete an entry." },
387         { "has_key", (PyCFunction)obj_has_key, METH_VARARGS, "S.has_key(key) -> None\n"
388                 "Check whether key exists in this database." },
389         { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None"
390                 "Store data." },
391         { "add_flags", (PyCFunction)obj_add_flags, METH_VARARGS, "S.add_flags(flags) -> None" },
392         { "remove_flags", (PyCFunction)obj_remove_flags, METH_VARARGS, "S.remove_flags(flags) -> None" },
393         { "iterkeys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" },
394         { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n"
395                 "Wipe the entire database." },
396         { "enable_seqnum", (PyCFunction)obj_enable_seqnum, METH_NOARGS,
397                 "S.enable_seqnum() -> None" },
398         { "increment_seqnum_nonblock", (PyCFunction)obj_increment_seqnum_nonblock, METH_NOARGS,
399                 "S.increment_seqnum_nonblock() -> None" },
400         { NULL }
401 };
402
403 static PyObject *obj_get_hash_size(PyTdbObject *self, void *closure)
404 {
405         return PyInt_FromLong(tdb_hash_size(self->ctx));
406 }
407
408 static int obj_set_max_dead(PyTdbObject *self, PyObject *max_dead, void *closure)
409 {
410         if (!PyInt_Check(max_dead))
411                 return -1;
412         tdb_set_max_dead(self->ctx, PyInt_AsLong(max_dead));
413         return 0;
414 }
415
416 static PyObject *obj_get_map_size(PyTdbObject *self, void *closure)
417 {
418         return PyInt_FromLong(tdb_map_size(self->ctx));
419 }
420
421 static PyObject *obj_get_freelist_size(PyTdbObject *self, void *closure)
422 {
423         return PyInt_FromLong(tdb_freelist_size(self->ctx));
424 }
425
426 static PyObject *obj_get_flags(PyTdbObject *self, void *closure)
427 {
428         return PyInt_FromLong(tdb_get_flags(self->ctx));
429 }
430
431 static PyObject *obj_get_filename(PyTdbObject *self, void *closure)
432 {
433         return PyString_FromString(tdb_name(self->ctx));
434 }
435
436 static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure)
437 {
438         return PyInt_FromLong(tdb_get_seqnum(self->ctx));
439 }
440
441
442 static PyGetSetDef tdb_object_getsetters[] = {
443         { (char *)"hash_size", (getter)obj_get_hash_size, NULL, NULL },
444         { (char *)"map_size", (getter)obj_get_map_size, NULL, NULL },
445         { (char *)"freelist_size", (getter)obj_get_freelist_size, NULL, NULL },
446         { (char *)"flags", (getter)obj_get_flags, NULL, NULL },
447         { (char *)"max_dead", NULL, (setter)obj_set_max_dead, NULL },
448         { (char *)"filename", (getter)obj_get_filename, NULL, (char *)"The filename of this TDB file."},
449         { (char *)"seqnum", (getter)obj_get_seqnum, NULL, NULL },
450         { NULL }
451 };
452
453 static PyObject *tdb_object_repr(PyTdbObject *self)
454 {
455         if (tdb_get_flags(self->ctx) & TDB_INTERNAL) {
456                 return PyString_FromString("Tdb(<internal>)");
457         } else {
458                 return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx));
459         }
460 }
461
462 static void tdb_object_dealloc(PyTdbObject *self)
463 {
464         if (!self->closed)
465                 tdb_close(self->ctx);
466         PyObject_Del(self);
467 }
468
469 static PyObject *obj_getitem(PyTdbObject *self, PyObject *key)
470 {
471         TDB_DATA tkey, val;
472         if (!PyString_Check(key)) {
473                 PyErr_SetString(PyExc_TypeError, "Expected string as key");
474                 return NULL;
475         }
476
477         tkey.dptr = (unsigned char *)PyString_AsString(key);
478         tkey.dsize = PyString_Size(key);
479
480         val = tdb_fetch(self->ctx, tkey);
481         if (val.dptr == NULL) {
482                 PyErr_SetString(PyExc_KeyError, "No such TDB entry");
483                 return NULL;
484         } else {
485                 return PyString_FromTDB_DATA(val);
486         }
487 }
488
489 static int obj_setitem(PyTdbObject *self, PyObject *key, PyObject *value)
490 {
491         TDB_DATA tkey, tval;
492         int ret;
493         if (!PyString_Check(key)) {
494                 PyErr_SetString(PyExc_TypeError, "Expected string as key");
495                 return -1;
496         }
497
498         tkey = PyString_AsTDB_DATA(key);
499
500         if (value == NULL) { 
501                 ret = tdb_delete(self->ctx, tkey);
502         } else { 
503                 if (!PyString_Check(value)) {
504                         PyErr_SetString(PyExc_TypeError, "Expected string as value");
505                         return -1;
506                 }
507
508                 tval = PyString_AsTDB_DATA(value);
509
510                 ret = tdb_store(self->ctx, tkey, tval, TDB_REPLACE);
511         }
512
513         if (ret != 0) {
514                 PyErr_SetTDBError(self->ctx);
515                 return -1;
516         } 
517
518         return ret;
519 }
520
521 static PyMappingMethods tdb_object_mapping = {
522         .mp_subscript = (binaryfunc)obj_getitem,
523         .mp_ass_subscript = (objobjargproc)obj_setitem,
524 };
525 PyTypeObject PyTdb = {
526         .tp_name = "Tdb",
527         .tp_basicsize = sizeof(PyTdbObject),
528         .tp_methods = tdb_object_methods,
529         .tp_getset = tdb_object_getsetters,
530         .tp_new = py_tdb_open,
531         .tp_doc = "A TDB file",
532         .tp_repr = (reprfunc)tdb_object_repr,
533         .tp_dealloc = (destructor)tdb_object_dealloc,
534         .tp_as_mapping = &tdb_object_mapping,
535         .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER,
536         .tp_iter = (getiterfunc)tdb_object_iter,
537 };
538
539 static PyMethodDef tdb_methods[] = {
540         { "open", (PyCFunction)py_tdb_open, METH_VARARGS|METH_KEYWORDS, "open(name, hash_size=0, tdb_flags=TDB_DEFAULT, flags=O_RDWR, mode=0600)\n"
541                 "Open a TDB file." },
542         { NULL }
543 };
544
545 void inittdb(void)
546 {
547         PyObject *m;
548
549         if (PyType_Ready(&PyTdb) < 0)
550                 return;
551
552         if (PyType_Ready(&PyTdbIterator) < 0)
553                 return;
554
555         m = Py_InitModule3("tdb", tdb_methods, "TDB is a simple key-value database similar to GDBM that supports multiple writers.");
556         if (m == NULL)
557                 return;
558
559         PyModule_AddObject(m, "REPLACE", PyInt_FromLong(TDB_REPLACE));
560         PyModule_AddObject(m, "INSERT", PyInt_FromLong(TDB_INSERT));
561         PyModule_AddObject(m, "MODIFY", PyInt_FromLong(TDB_MODIFY));
562
563         PyModule_AddObject(m, "DEFAULT", PyInt_FromLong(TDB_DEFAULT));
564         PyModule_AddObject(m, "CLEAR_IF_FIRST", PyInt_FromLong(TDB_CLEAR_IF_FIRST));
565         PyModule_AddObject(m, "INTERNAL", PyInt_FromLong(TDB_INTERNAL));
566         PyModule_AddObject(m, "NOLOCK", PyInt_FromLong(TDB_NOLOCK));
567         PyModule_AddObject(m, "NOMMAP", PyInt_FromLong(TDB_NOMMAP));
568         PyModule_AddObject(m, "CONVERT", PyInt_FromLong(TDB_CONVERT));
569         PyModule_AddObject(m, "BIGENDIAN", PyInt_FromLong(TDB_BIGENDIAN));
570         PyModule_AddObject(m, "NOSYNC", PyInt_FromLong(TDB_NOSYNC));
571         PyModule_AddObject(m, "SEQNUM", PyInt_FromLong(TDB_SEQNUM));
572         PyModule_AddObject(m, "VOLATILE", PyInt_FromLong(TDB_VOLATILE));
573         PyModule_AddObject(m, "ALLOW_NESTING", PyInt_FromLong(TDB_ALLOW_NESTING));
574         PyModule_AddObject(m, "DISALLOW_NESTING", PyInt_FromLong(TDB_DISALLOW_NESTING));
575         PyModule_AddObject(m, "INCOMPATIBLE_HASH", PyInt_FromLong(TDB_INCOMPATIBLE_HASH));
576
577         PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
578
579         Py_INCREF(&PyTdb);
580         PyModule_AddObject(m, "Tdb", (PyObject *)&PyTdb);
581
582         Py_INCREF(&PyTdbIterator);
583 }