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