blob: d6102e1993db294f7171a018bc962dcee24c3386 [file] [log] [blame]
Guido van Rossum1100dca1995-08-30 23:43:03 +00001/***********************************************************
2Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
3The Netherlands.
4
5 All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Stichting Mathematisch
12Centrum or CWI not be used in advertising or publicity pertaining to
13distribution of the software without specific, written prior permission.
14
15STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23******************************************************************/
24
25/* Berkeley DB interface.
26 Author: Michael McLay
27 Hacked: Guido van Rossum
28 Btree and Recno additions plus sequence methods: David Ely
29
30 XXX To do:
31 - provide interface to the B-tree and record libraries too
32 - provide a way to access the various hash functions
33 - support more open flags
34 */
35
36#include "allobjects.h"
37#include "modsupport.h"
38
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <fcntl.h>
42#include <db.h>
43/* Please don't include internal header files of the Berkeley db package
44 (it messes up the info required in the Setup file) */
45
46typedef struct {
47 OB_HEAD
48 DB *di_bsddb;
49 int di_size; /* -1 means recompute */
50} bsddbobject;
51
52staticforward typeobject Bsddbtype;
53
54#define is_bsddbobject(v) ((v)->ob_type == &Bsddbtype)
55
56static object *BsddbError;
57
58static object *
59newdbhashobject(file, flags, mode,
60 bsize, ffactor, nelem, cachesize, hash, lorder)
61 char *file;
62 int flags;
63 int mode;
64 int bsize;
65 int ffactor;
66 int nelem;
67 int cachesize;
68 int hash; /* XXX ignored */
69 int lorder;
70{
71 bsddbobject *dp;
72 HASHINFO info;
73
74 if ((dp = NEWOBJ(bsddbobject, &Bsddbtype)) == NULL)
75 return NULL;
76
77 info.bsize = bsize;
78 info.ffactor = ffactor;
79 info.nelem = nelem;
80 info.cachesize = cachesize;
81 info.hash = NULL; /* XXX should derive from hash argument */
82 info.lorder = lorder;
83
84 if ((dp->di_bsddb = dbopen(file, flags, mode, DB_HASH, &info)) == NULL) {
85 err_errno(BsddbError);
86 DECREF(dp);
87 return NULL;
88 }
89
90 dp->di_size = -1;
91
92 return (object *)dp;
93}
94
95static object *
96newdbbtobject(file, flags, mode,
97 btflags, cachesize, maxkeypage, minkeypage, psize, lorder)
98 char *file;
99 int flags;
100 int mode;
101 int btflags;
102 int cachesize;
103 int maxkeypage;
104 int minkeypage;
105 int psize;
106 int lorder;
107{
108 bsddbobject *dp;
109 BTREEINFO info;
110
111 if ((dp = NEWOBJ(bsddbobject, &Bsddbtype)) == NULL)
112 return NULL;
113
114 info.flags = btflags;
115 info.cachesize = cachesize;
116 info.maxkeypage = maxkeypage;
117 info.minkeypage = minkeypage;
118 info.psize = psize;
119 info.lorder = lorder;
120 info.compare = 0; /* Use default comparison functions, for now..*/
121 info.prefix = 0;
122
123 if ((dp->di_bsddb = dbopen(file, flags, mode, DB_BTREE, &info)) == NULL) {
124 err_errno(BsddbError);
125 DECREF(dp);
126 return NULL;
127 }
128
129 dp->di_size = -1;
130
131 return (object *)dp;
132}
133
134static object *
135newdbrnobject(file, flags, mode,
136 rnflags, cachesize, psize, lorder, reclen, bval, bfname)
137 char *file;
138 int flags;
139 int mode;
140 int rnflags;
141 int cachesize;
142 int psize;
143 int lorder;
144 size_t reclen;
145 u_char bval;
146 char *bfname;
147{
148 bsddbobject *dp;
149 RECNOINFO info;
150
151 if ((dp = NEWOBJ(bsddbobject, &Bsddbtype)) == NULL)
152 return NULL;
153
154 info.flags = rnflags;
155 info.cachesize = cachesize;
156 info.psize = psize;
157 info.lorder = lorder;
158 info.reclen = reclen;
159 info.bval = bval;
160 info.bfname = bfname;
161
162 if ((dp->di_bsddb = dbopen(file, flags, mode, DB_RECNO, &info)) == NULL) {
163 err_errno(BsddbError);
164 DECREF(dp);
165 return NULL;
166 }
167
168 dp->di_size = -1;
169
170 return (object *)dp;
171}
172
173
174static void
175bsddb_dealloc(dp)
176 bsddbobject *dp;
177{
178 if (dp->di_bsddb != NULL) {
179 if ((dp->di_bsddb->close)(dp->di_bsddb) != 0)
180 fprintf(stderr,
181 "Python bsddb: close errno %s in dealloc\n", errno);
182 }
183 DEL(dp);
184}
185
186static int
187bsddb_length(dp)
188 bsddbobject *dp;
189{
190 if (dp->di_size < 0) {
191 DBT krec, drec;
192 int status;
193 int size = 0;
194 for (status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec,R_FIRST);
195 status == 0;
196 status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec, R_NEXT))
197 size++;
198 if (status < 0) {
199 err_errno(BsddbError);
200 return -1;
201 }
202 dp->di_size = size;
203 }
204 return dp->di_size;
205}
206
207static object *
208bsddb_subscript(dp, key)
209 bsddbobject *dp;
210 object *key;
211{
212 int status;
213 object *v;
214 DBT krec, drec;
215 char *data;
216 int size;
217
218 if (!getargs(key, "s#", &data, &size))
219 return NULL;
220 krec.data = data;
221 krec.size = size;
222
223 status = (dp->di_bsddb->get)(dp->di_bsddb, &krec, &drec, 0);
224 if (status != 0) {
225 if (status < 0)
226 err_errno(BsddbError);
227 else
228 err_setval(KeyError, key);
229 return NULL;
230 }
231
232 return newsizedstringobject((char *)drec.data, (int)drec.size);
233}
234
235static int
236bsddb_ass_sub(dp, key, value)
237 bsddbobject *dp;
238 object *key, *value;
239{
240 int status;
241 DBT krec, drec;
242 char *data;
243 int size;
244
245 if (!getargs(key, "s#", &data, &size)) {
246 err_setstr(TypeError, "bsddb key type must be string");
247 return -1;
248 }
249 krec.data = data;
250 krec.size = size;
251 dp->di_size = -1;
252 if (value == NULL) {
253 status = (dp->di_bsddb->del)(dp->di_bsddb, &krec, 0);
254 }
255 else {
256 if (!getargs(value, "s#", &data, &size)) {
257 err_setstr(TypeError, "bsddb value type must be string");
258 return -1;
259 }
260 drec.data = data;
261 drec.size = size;
262 status = (dp->di_bsddb->put)(dp->di_bsddb, &krec, &drec, 0);
263 }
264 if (status != 0) {
265 if (status < 0)
266 err_errno(BsddbError);
267 else
268 err_setval(KeyError, key);
269 return -1;
270 }
271 return 0;
272}
273
274static mapping_methods bsddb_as_mapping = {
275 (inquiry)bsddb_length, /*mp_length*/
276 (binaryfunc)bsddb_subscript, /*mp_subscript*/
277 (objobjargproc)bsddb_ass_sub, /*mp_ass_subscript*/
278};
279
280static object *
281bsddb_close(dp, args)
282 bsddbobject *dp;
283 object *args;
284{
285 if (!getnoarg(args))
286 return NULL;
287 if (dp->di_bsddb != NULL) {
288 if ((dp->di_bsddb->close)(dp->di_bsddb) != 0) {
289 dp->di_bsddb = NULL;
290 err_errno(BsddbError);
291 return NULL;
292 }
293 }
294 dp->di_bsddb = NULL;
295 INCREF(None);
296 return None;
297}
298
299static object *
300bsddb_keys(dp, args)
301 bsddbobject *dp;
302 object *args;
303{
304 object *list, *item;
305 DBT krec, drec;
306 int status;
307 int err;
308
309 if (!getnoarg(args))
310 return NULL;
311 list = newlistobject(0);
312 if (list == NULL)
313 return NULL;
314 for (status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec, R_FIRST);
315 status == 0;
316 status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec, R_NEXT)) {
317 item = newsizedstringobject((char *)krec.data, (int)krec.size);
318 if (item == NULL) {
319 DECREF(list);
320 return NULL;
321 }
322 err = addlistitem(list, item);
323 DECREF(item);
324 if (err != 0) {
325 DECREF(list);
326 return NULL;
327 }
328 }
329 if (status < 0) {
330 err_errno(BsddbError);
331 DECREF(list);
332 return NULL;
333 }
334 if (dp->di_size < 0)
335 dp->di_size = getlistsize(list); /* We just did the work for this... */
336 return list;
337}
338
339static object *
340bsddb_has_key(dp, args)
341 bsddbobject *dp;
342 object *args;
343{
344 DBT krec, drec;
345 int status;
346 char *data;
347 int size;
348
349 if (!getargs(args, "s#", &data, &size))
350 return NULL;
351 krec.data = data;
352 krec.size = size;
353
354 status = (dp->di_bsddb->get)(dp->di_bsddb, &krec, &drec, 0);
355 if (status < 0) {
356 err_errno(BsddbError);
357 return NULL;
358 }
359
360 return newintobject(status == 0);
361}
362
363static object *
364bsddb_set_location(dp, key)
365 bsddbobject *dp;
366 object *key;
367{
368 int status;
369 object *v;
370 DBT krec, drec;
371 char *data;
372 int size;
373
374 if (!getargs(key, "s#", &data, &size))
375 return NULL;
376 krec.data = data;
377 krec.size = size;
378
379 status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec, R_CURSOR);
380 if (status != 0) {
381 if (status < 0)
382 err_errno(BsddbError);
383 else
384 err_setval(KeyError, key);
385 return NULL;
386 }
387
388 return mkvalue("s#s#", krec.data, krec.size, drec.data, drec.size);
389}
390
391static object *
392bsddb_seq(dp, args, sequence_request)
393 bsddbobject *dp;
394 object *args;
395 int sequence_request;
396{
397 int status;
398 DBT krec, drec;
399
400 if (!getnoarg(args))
401 return NULL;
402
403 krec.data = 0;
404 krec.size = 0;
405
406 status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec, sequence_request);
407 if (status != 0) {
408 if (status < 0)
409 err_errno(BsddbError);
410 else
411 err_setval(KeyError, args);
412 return NULL;
413 }
414
415 return mkvalue("s#s#", krec.data, krec.size, drec.data, drec.size);
416}
417
418static object *
419bsddb_next(dp, key)
420 bsddbobject *dp;
421 object *key;
422{
423 return bsddb_seq(dp, key, R_NEXT);
424}
425static object *
426bsddb_previous(dp, key)
427 bsddbobject *dp;
428 object *key;
429{
430 return bsddb_seq(dp, key, R_PREV);
431}
432static object *
433bsddb_first(dp, key)
434 bsddbobject *dp;
435 object *key;
436{
437 return bsddb_seq(dp, key, R_FIRST);
438}
439static object *
440bsddb_last(dp, key)
441 bsddbobject *dp;
442 object *key;
443{
444 return bsddb_seq(dp, key, R_LAST);
445}
446static object *
447bsddb_sync(dp, args)
448 bsddbobject *dp;
449 object *args;
450{
451 int status;
452
453 status = (dp->di_bsddb->sync)(dp->di_bsddb, 0);
454 if (status != 0) {
455 err_errno(BsddbError);
456 return NULL;
457 }
458 return newintobject(status = 0);
459}
460static struct methodlist bsddb_methods[] = {
461 {"close", (method)bsddb_close},
462 {"keys", (method)bsddb_keys},
463 {"has_key", (method)bsddb_has_key},
464 {"set_location", (method)bsddb_set_location},
465 {"next", (method)bsddb_next},
466 {"previous", (method)bsddb_previous},
467 {"first", (method)bsddb_first},
468 {"last", (method)bsddb_last},
469 {"sync", (method)bsddb_sync},
470 {NULL, NULL} /* sentinel */
471};
472
473static object *
474bsddb_getattr(dp, name)
475 object *dp;
476 char *name;
477{
478 return findmethod(bsddb_methods, dp, name);
479}
480
481static typeobject Bsddbtype = {
482 OB_HEAD_INIT(&Typetype)
483 0,
484 "bsddb",
485 sizeof(bsddbobject),
486 0,
487 (destructor)bsddb_dealloc, /*tp_dealloc*/
488 0, /*tp_print*/
489 (getattrfunc)bsddb_getattr, /*tp_getattr*/
490 0, /*tp_setattr*/
491 0, /*tp_compare*/
492 0, /*tp_repr*/
493 0, /*tp_as_number*/
494 0, /*tp_as_sequence*/
495 &bsddb_as_mapping, /*tp_as_mapping*/
496};
497
498static object *
499bsdhashopen(self, args)
500 object *self;
501 object *args;
502{
503 char *file;
504 char *flag = NULL;
505 int flags = O_RDONLY;
506 int mode = 0666;
507 int bsize = 0;
508 int ffactor = 0;
509 int nelem = 0;
510 int cachesize = 0;
511 int hash = 0; /* XXX currently ignored */
512 int lorder = 0;
513
514 if (!newgetargs(args, "s|siiiiiii",
515 &file, &flag, &mode,
516 &bsize, &ffactor, &nelem, &cachesize, &hash, &lorder))
517 return NULL;
518 if (flag != NULL) {
519 /* XXX need a way to pass O_EXCL, O_EXLOCK, O_NONBLOCK, O_SHLOCK */
520 if (flag[0] == 'r')
521 flags = O_RDONLY;
522 else if (flag[0] == 'w')
523 flags = O_RDWR;
524 else if (flag[0] == 'c')
525 flags = O_RDWR|O_CREAT;
526 else if (flag[0] == 'n')
527 flags = O_RDWR|O_CREAT|O_TRUNC;
528 else {
529 err_setstr(BsddbError,
530 "Flag should begin with 'r', 'w', 'c' or 'n'");
531 return NULL;
532 }
533 if (flag[1] == 'l') {
534#if defined(O_EXLOCK) && defined(O_SHLOCK)
535 if (flag[0] == 'r')
536 flags |= O_SHLOCK;
537 else
538 flags |= O_EXLOCK;
539#else
540 err_setstr(BsddbError, "locking not supported on this platform");
541 return NULL;
542#endif
543 }
544 }
545 return newdbhashobject(file, flags, mode,
546 bsize, ffactor, nelem, cachesize, hash, lorder);
547}
548
549static object *
550bsdbtopen(self, args)
551 object *self;
552 object *args;
553{
554 char *file;
555 char *flag = NULL;
556 int flags = O_RDONLY;
557 int mode = 0666;
558 int cachesize = 0;
559 int maxkeypage;
560 int minkeypage;
561 int btflags;
562 unsigned int psize;
563 int lorder;
564
565 if (!newgetargs(args, "s|siiiiiii",
566 &file, &flag, &mode,
567 &btflags, &cachesize, &maxkeypage, &minkeypage, &psize, &lorder))
568 return NULL;
569 if (flag != NULL) {
570 /* XXX need a way to pass O_EXCL, O_EXLOCK, O_NONBLOCK, O_SHLOCK */
571 if (flag[0] == 'r')
572 flags = O_RDONLY;
573 else if (flag[0] == 'w')
574 flags = O_RDWR;
575 else if (flag[0] == 'c')
576 flags = O_RDWR|O_CREAT;
577 else if (flag[0] == 'n')
578 flags = O_RDWR|O_CREAT|O_TRUNC;
579 else {
580 err_setstr(BsddbError,
581 "Flag should begin with 'r', 'w', 'c' or 'n'");
582 return NULL;
583 }
584 if (flag[1] == 'l') {
585#if defined(O_EXLOCK) && defined(O_SHLOCK)
586 if (flag[0] == 'r')
587 flags |= O_SHLOCK;
588 else
589 flags |= O_EXLOCK;
590#else
591 err_setstr(BsddbError, "locking not supported on this platform");
592 return NULL;
593#endif
594 }
595 }
596 return newdbbtobject(file, flags, mode,
597 btflags, cachesize, maxkeypage, minkeypage, psize, lorder);
598}
599static object *
600bsdrnopen(self, args)
601 object *self;
602 object *args;
603{
604 char *file;
605 char *flag = NULL;
606 int flags = O_RDONLY;
607 int mode = 0666;
608 int cachesize = 0;
609 int rnflags;
610 unsigned int psize;
611 int lorder;
612 size_t reclen;
613 char *bval;
614 char *bfname;
615
616 if (!newgetargs(args, "s|siiiiiiss",
617 &file, &flag, &mode,
618 &rnflags, &cachesize, &psize, &lorder, &reclen, &bval, &bfname))
619 return NULL;
620 if (flag != NULL) {
621 /* XXX need a way to pass O_EXCL, O_EXLOCK, O_NONBLOCK, O_SHLOCK */
622 if (flag[0] == 'r')
623 flags = O_RDONLY;
624 else if (flag[0] == 'w')
625 flags = O_RDWR;
626 else if (flag[0] == 'c')
627 flags = O_RDWR|O_CREAT;
628 else if (flag[0] == 'n')
629 flags = O_RDWR|O_CREAT|O_TRUNC;
630 else {
631 err_setstr(BsddbError,
632 "Flag should begin with 'r', 'w', 'c' or 'n'");
633 return NULL;
634 }
635 if (flag[1] == 'l') {
636#if defined(O_EXLOCK) && defined(O_SHLOCK)
637 if (flag[0] == 'r')
638 flags |= O_SHLOCK;
639 else
640 flags |= O_EXLOCK;
641#else
642 err_setstr(BsddbError, "locking not supported on this platform");
643 return NULL;
644#endif
645 }
646 }
647 return newdbrnobject(file, flags, mode,
648 rnflags, cachesize, psize, lorder, bval[0], bfname);
649}
650
651static struct methodlist bsddbmodule_methods[] = {
652 {"hashopen", (method)bsdhashopen, 1},
653 {"btopen", (method)bsdbtopen, 1},
654 {"rnopen", (method)bsdrnopen, 1},
655 {0, 0},
656};
657
658void
659initbsddb() {
660 object *m, *d;
661
662 m = initmodule("bsddb", bsddbmodule_methods);
663 d = getmoduledict(m);
664 BsddbError = newstringobject("bsddb.error");
665 if (BsddbError == NULL || dictinsert(d, "error", BsddbError))
666 fatal("can't define bsddb.error");
667}