_gdbmmodule.c 15.5 KB
Newer Older
1 2

/* DBM module using dictionary interface */
3 4
/* Author: Anthony Baxter, after dbmmodule.c */
/* Doc strings: Mitch Chapman */
5 6


7
#include "Python.h"
8 9 10 11 12 13

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "gdbm.h"

14
#if defined(WIN32) && !defined(__CYGWIN__)
15 16 17 18
#include "gdbmerrno.h"
extern const char * gdbm_strerror(gdbm_error);
#endif

19 20
PyDoc_STRVAR(gdbmmodule__doc__,
"This module provides an interface to the GNU DBM (GDBM) library.\n\
21 22 23 24 25 26 27 28
\n\
This module is quite similar to the dbm module, but uses GDBM instead to\n\
provide some additional functionality. Please note that the file formats\n\
created by GDBM and dbm are incompatible. \n\
\n\
GDBM objects behave like mappings (dictionaries), except that keys and\n\
values are always strings. Printing a GDBM object doesn't print the\n\
keys and values, and the items() and values() methods are not\n\
29
supported.");
30

31
typedef struct {
Fred Drake's avatar
Fred Drake committed
32
    PyObject_HEAD
33
    int di_size;        /* -1 means recompute */
Fred Drake's avatar
Fred Drake committed
34
    GDBM_FILE di_dbm;
35 36
} dbmobject;

37
static PyTypeObject Dbmtype;
38

39
#define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype)
40
#define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \
Fred Drake's avatar
Fred Drake committed
41 42
    { PyErr_SetString(DbmError, "GDBM object has already been closed"); \
      return NULL; }
43 44


45

46
static PyObject *DbmError;
47

48 49
PyDoc_STRVAR(gdbm_object__doc__,
"This object represents a GDBM database.\n\
50 51 52 53 54 55
GDBM objects behave like mappings (dictionaries), except that keys and\n\
values are always strings. Printing a GDBM object doesn't print the\n\
keys and values, and the items() and values() methods are not\n\
supported.\n\
\n\
GDBM objects also support additional operations such as firstkey,\n\
56
nextkey, reorganize, and sync.");
57

58
static PyObject *
Fred Drake's avatar
Fred Drake committed
59
newdbmobject(char *file, int flags, int mode)
60
{
Fred Drake's avatar
Fred Drake committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    dbmobject *dp;

    dp = PyObject_New(dbmobject, &Dbmtype);
    if (dp == NULL)
        return NULL;
    dp->di_size = -1;
    errno = 0;
    if ((dp->di_dbm = gdbm_open(file, 0, flags, mode, NULL)) == 0) {
        if (errno != 0)
            PyErr_SetFromErrno(DbmError);
        else
            PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
        Py_DECREF(dp);
        return NULL;
    }
    return (PyObject *)dp;
77 78 79 80 81
}

/* Methods */

static void
Fred Drake's avatar
Fred Drake committed
82
dbm_dealloc(register dbmobject *dp)
83
{
Fred Drake's avatar
Fred Drake committed
84 85 86
    if (dp->di_dbm)
        gdbm_close(dp->di_dbm);
    PyObject_Del(dp);
87 88
}

Martin v. Löwis's avatar
Martin v. Löwis committed
89
static Py_ssize_t
Fred Drake's avatar
Fred Drake committed
90
dbm_length(dbmobject *dp)
91
{
Fred Drake's avatar
Fred Drake committed
92
    if (dp->di_dbm == NULL) {
93 94
        PyErr_SetString(DbmError, "GDBM object has already been closed");
        return -1;
Fred Drake's avatar
Fred Drake committed
95 96 97 98 99
    }
    if (dp->di_size < 0) {
        datum key,okey;
        int size;
        okey.dsize=0;
100
        okey.dptr=NULL;
Fred Drake's avatar
Fred Drake committed
101 102 103 104 105 106 107

        size = 0;
        for (key=gdbm_firstkey(dp->di_dbm); key.dptr;
             key = gdbm_nextkey(dp->di_dbm,okey)) {
            size++;
            if(okey.dsize) free(okey.dptr);
            okey=key;
108
        }
Fred Drake's avatar
Fred Drake committed
109 110 111
        dp->di_size = size;
    }
    return dp->di_size;
112 113
}

114
static PyObject *
Fred Drake's avatar
Fred Drake committed
115
dbm_subscript(dbmobject *dp, register PyObject *key)
116
{
Fred Drake's avatar
Fred Drake committed
117 118 119 120 121 122 123 124 125 126 127 128 129
    PyObject *v;
    datum drec, krec;

    if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize) )
        return NULL;

    if (dp->di_dbm == NULL) {
        PyErr_SetString(DbmError,
                        "GDBM object has already been closed");
        return NULL;
    }
    drec = gdbm_fetch(dp->di_dbm, krec);
    if (drec.dptr == 0) {
130
        PyErr_SetObject(PyExc_KeyError, key);
Fred Drake's avatar
Fred Drake committed
131 132
        return NULL;
    }
133
    v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
Fred Drake's avatar
Fred Drake committed
134 135
    free(drec.dptr);
    return v;
136 137 138
}

static int
Fred Drake's avatar
Fred Drake committed
139
dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
140
{
Fred Drake's avatar
Fred Drake committed
141 142 143 144
    datum krec, drec;

    if (!PyArg_Parse(v, "s#", &krec.dptr, &krec.dsize) ) {
        PyErr_SetString(PyExc_TypeError,
145
                        "gdbm mappings have bytes or string indices only");
Fred Drake's avatar
Fred Drake committed
146 147 148 149
        return -1;
    }
    if (dp->di_dbm == NULL) {
        PyErr_SetString(DbmError,
150 151
                        "GDBM object has already been closed");
        return -1;
Fred Drake's avatar
Fred Drake committed
152 153 154 155
    }
    dp->di_size = -1;
    if (w == NULL) {
        if (gdbm_delete(dp->di_dbm, krec) < 0) {
156
            PyErr_SetObject(PyExc_KeyError, v);
Fred Drake's avatar
Fred Drake committed
157
            return -1;
158
        }
Fred Drake's avatar
Fred Drake committed
159 160 161 162
    }
    else {
        if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) {
            PyErr_SetString(PyExc_TypeError,
163
                            "gdbm mappings have byte or string elements only");
Fred Drake's avatar
Fred Drake committed
164 165 166 167 168 169 170 171 172 173 174 175 176
            return -1;
        }
        errno = 0;
        if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
            if (errno != 0)
                PyErr_SetFromErrno(DbmError);
            else
                PyErr_SetString(DbmError,
                                gdbm_strerror(gdbm_errno));
            return -1;
        }
    }
    return 0;
177 178
}

179
static PyMappingMethods dbm_as_mapping = {
180
    (lenfunc)dbm_length,                /*mp_length*/
Fred Drake's avatar
Fred Drake committed
181 182
    (binaryfunc)dbm_subscript,          /*mp_subscript*/
    (objobjargproc)dbm_ass_sub,         /*mp_ass_subscript*/
183 184
};

185 186 187
PyDoc_STRVAR(dbm_close__doc__,
"close() -> None\n\
Closes the database.");
188

189
static PyObject *
190
dbm_close(register dbmobject *dp, PyObject *unused)
191
{
Fred Drake's avatar
Fred Drake committed
192 193 194 195 196
    if (dp->di_dbm)
        gdbm_close(dp->di_dbm);
    dp->di_dbm = NULL;
    Py_INCREF(Py_None);
    return Py_None;
197 198
}

199
/* XXX Should return a set or a set view */
200 201 202
PyDoc_STRVAR(dbm_keys__doc__,
"keys() -> list_of_keys\n\
Get a list of all keys in the database.");
203

204
static PyObject *
205
dbm_keys(register dbmobject *dp, PyObject *unused)
206
{
Fred Drake's avatar
Fred Drake committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    register PyObject *v, *item;
    datum key, nextkey;
    int err;

    if (dp == NULL || !is_dbmobject(dp)) {
        PyErr_BadInternalCall();
        return NULL;
    }
    check_dbmobject_open(dp);

    v = PyList_New(0);
    if (v == NULL)
        return NULL;

    key = gdbm_firstkey(dp->di_dbm);
    while (key.dptr) {
223
        item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
Fred Drake's avatar
Fred Drake committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
        if (item == NULL) {
            free(key.dptr);
            Py_DECREF(v);
            return NULL;
        }
        err = PyList_Append(v, item);
        Py_DECREF(item);
        if (err != 0) {
            free(key.dptr);
            Py_DECREF(v);
            return NULL;
        }
        nextkey = gdbm_nextkey(dp->di_dbm, key);
        free(key.dptr);
        key = nextkey;
    }
    return v;
241 242
}

243 244
static int
dbm_contains(PyObject *self, PyObject *arg)
245
{
246
    dbmobject *dp = (dbmobject *)self;
Fred Drake's avatar
Fred Drake committed
247 248
    datum key;

249
    if ((dp)->di_dbm == NULL) {
250 251 252
        PyErr_SetString(DbmError,
                        "GDBM object has already been closed");
        return -1;
253
    }
254
    if (!PyBytes_Check(arg)) {
255 256 257 258
        PyErr_Format(PyExc_TypeError,
                     "gdbm key must be bytes, not %.100s",
                     arg->ob_type->tp_name);
        return -1;
259
    }
260 261
    key.dptr = PyBytes_AS_STRING(arg);
    key.dsize = PyBytes_GET_SIZE(arg);
262
    return gdbm_exists(dp->di_dbm, key);
263 264
}

265
static PySequenceMethods dbm_as_sequence = {
266 267 268 269 270 271 272 273 274 275
        0,                      /* sq_length */
        0,                      /* sq_concat */
        0,                      /* sq_repeat */
        0,                      /* sq_item */
        0,                      /* sq_slice */
        0,                      /* sq_ass_item */
        0,                      /* sq_ass_slice */
        dbm_contains,           /* sq_contains */
        0,                      /* sq_inplace_concat */
        0,                      /* sq_inplace_repeat */
276 277
};

278 279
PyDoc_STRVAR(dbm_firstkey__doc__,
"firstkey() -> key\n\
280 281 282
It's possible to loop over every key in the database using this method\n\
and the nextkey() method. The traversal is ordered by GDBM's internal\n\
hash values, and won't be sorted by the key values. This method\n\
283
returns the starting key.");
284

285
static PyObject *
286
dbm_firstkey(register dbmobject *dp, PyObject *unused)
287
{
Fred Drake's avatar
Fred Drake committed
288 289 290 291 292 293
    register PyObject *v;
    datum key;

    check_dbmobject_open(dp);
    key = gdbm_firstkey(dp->di_dbm);
    if (key.dptr) {
294
        v = PyBytes_FromStringAndSize(key.dptr, key.dsize);
Fred Drake's avatar
Fred Drake committed
295 296 297 298 299 300 301
        free(key.dptr);
        return v;
    }
    else {
        Py_INCREF(Py_None);
        return Py_None;
    }
302 303
}

304 305
PyDoc_STRVAR(dbm_nextkey__doc__,
"nextkey(key) -> next_key\n\
306 307 308 309 310 311 312
Returns the key that follows key in the traversal.\n\
The following code prints every key in the database db, without having\n\
to create a list in memory that contains them all:\n\
\n\
      k = db.firstkey()\n\
      while k != None:\n\
          print k\n\
313
          k = db.nextkey(k)");
314

315
static PyObject *
Fred Drake's avatar
Fred Drake committed
316
dbm_nextkey(register dbmobject *dp, PyObject *args)
317
{
Fred Drake's avatar
Fred Drake committed
318 319 320 321 322 323 324 325
    register PyObject *v;
    datum key, nextkey;

    if (!PyArg_ParseTuple(args, "s#:nextkey", &key.dptr, &key.dsize))
        return NULL;
    check_dbmobject_open(dp);
    nextkey = gdbm_nextkey(dp->di_dbm, key);
    if (nextkey.dptr) {
326
        v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize);
Fred Drake's avatar
Fred Drake committed
327 328 329 330 331 332 333
        free(nextkey.dptr);
        return v;
    }
    else {
        Py_INCREF(Py_None);
        return Py_None;
    }
334 335
}

336 337
PyDoc_STRVAR(dbm_reorganize__doc__,
"reorganize() -> None\n\
338 339 340 341
If you have carried out a lot of deletions and would like to shrink\n\
the space used by the GDBM file, this routine will reorganize the\n\
database. GDBM will not shorten the length of a database file except\n\
by using this reorganization; otherwise, deleted file space will be\n\
342
kept and reused as new (key,value) pairs are added.");
343

344
static PyObject *
345
dbm_reorganize(register dbmobject *dp, PyObject *unused)
346
{
Fred Drake's avatar
Fred Drake committed
347 348 349 350 351 352 353 354 355 356 357
    check_dbmobject_open(dp);
    errno = 0;
    if (gdbm_reorganize(dp->di_dbm) < 0) {
        if (errno != 0)
            PyErr_SetFromErrno(DbmError);
        else
            PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
        return NULL;
    }
    Py_INCREF(Py_None);
    return Py_None;
358 359
}

360 361
PyDoc_STRVAR(dbm_sync__doc__,
"sync() -> None\n\
362
When the database has been opened in fast mode, this method forces\n\
363
any unwritten data to be written to the disk.");
364

365
static PyObject *
366
dbm_sync(register dbmobject *dp, PyObject *unused)
367
{
Fred Drake's avatar
Fred Drake committed
368 369 370 371
    check_dbmobject_open(dp);
    gdbm_sync(dp->di_dbm);
    Py_INCREF(Py_None);
    return Py_None;
372 373
}

374
static PyMethodDef dbm_methods[] = {
375 376
    {"close",     (PyCFunction)dbm_close,   METH_NOARGS, dbm_close__doc__},
    {"keys",      (PyCFunction)dbm_keys,    METH_NOARGS, dbm_keys__doc__},
377
    {"firstkey",  (PyCFunction)dbm_firstkey,METH_NOARGS, dbm_firstkey__doc__},
378
    {"nextkey",   (PyCFunction)dbm_nextkey, METH_VARARGS, dbm_nextkey__doc__},
379 380
    {"reorganize",(PyCFunction)dbm_reorganize,METH_NOARGS, dbm_reorganize__doc__},
    {"sync",      (PyCFunction)dbm_sync,    METH_NOARGS, dbm_sync__doc__},
381
    {NULL,              NULL}           /* sentinel */
382 383
};

384
static PyTypeObject Dbmtype = {
385
    PyVarObject_HEAD_INIT(0, 0)
386
    "_gdbm.gdbm",
Fred Drake's avatar
Fred Drake committed
387 388 389 390
    sizeof(dbmobject),
    0,
    (destructor)dbm_dealloc,            /*tp_dealloc*/
    0,                                  /*tp_print*/
391
    0,                                  /*tp_getattr*/
Fred Drake's avatar
Fred Drake committed
392
    0,                                  /*tp_setattr*/
393
    0,                                  /*tp_reserved*/
Fred Drake's avatar
Fred Drake committed
394 395
    0,                                  /*tp_repr*/
    0,                                  /*tp_as_number*/
396
    &dbm_as_sequence,                   /*tp_as_sequence*/
Fred Drake's avatar
Fred Drake committed
397 398 399 400 401 402 403
    &dbm_as_mapping,                    /*tp_as_mapping*/
    0,                                  /*tp_hash*/
    0,                                  /*tp_call*/
    0,                                  /*tp_str*/
    0,                                  /*tp_getattro*/
    0,                                  /*tp_setattro*/
    0,                                  /*tp_as_buffer*/
Christian Heimes's avatar
Christian Heimes committed
404
    Py_TPFLAGS_DEFAULT,                 /*tp_xxx4*/
Fred Drake's avatar
Fred Drake committed
405
    gdbm_object__doc__,                 /*tp_doc*/
406 407 408 409 410 411 412
    0,                                  /*tp_traverse*/
    0,                                  /*tp_clear*/
    0,                                  /*tp_richcompare*/
    0,                                  /*tp_weaklistoffset*/
    0,                                  /*tp_iter*/
    0,                                  /*tp_iternext*/
    dbm_methods,                        /*tp_methods*/
413 414 415 416
};

/* ----------------------------------------------------------------- */

417 418
PyDoc_STRVAR(dbmopen__doc__,
"open(filename, [flags, [mode]])  -> dbm_object\n\
419 420 421
Open a dbm database and return a dbm object. The filename argument is\n\
the name of the database file.\n\
\n\
422
The optional flags argument can be 'r' (to open an existing database\n\
423 424 425 426
for reading only -- default), 'w' (to open an existing database for\n\
reading and writing), 'c' (which creates the database if it doesn't\n\
exist), or 'n' (which always creates a new empty database).\n\
\n\
427 428 429 430 431 432 433 434 435 436
Some versions of gdbm support additional flags which must be\n\
appended to one of the flags described above. The module constant\n\
'open_flags' is a string of valid additional flags. The 'f' flag\n\
opens the database in fast mode; altered data will not automatically\n\
be written to the disk after every change. This results in faster\n\
writes to the database, but may result in an inconsistent database\n\
if the program crashes while the database is still open. Use the\n\
sync() method to force any unwritten data to be written to the disk.\n\
The 's' flag causes all database operations to be synchronized to\n\
disk. The 'u' flag disables locking of the database file.\n\
437 438
\n\
The optional mode argument is the Unix mode of the file, used only\n\
439
when the database has to be created. It defaults to octal 0666. ");
440

441
static PyObject *
Fred Drake's avatar
Fred Drake committed
442
dbmopen(PyObject *self, PyObject *args)
443
{
Fred Drake's avatar
Fred Drake committed
444
    char *name;
445
    char *flags = "r";
Fred Drake's avatar
Fred Drake committed
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
    int iflags;
    int mode = 0666;

    if (!PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode))
        return NULL;
    switch (flags[0]) {
    case 'r':
        iflags = GDBM_READER;
        break;
    case 'w':
        iflags = GDBM_WRITER;
        break;
    case 'c':
        iflags = GDBM_WRCREAT;
        break;
    case 'n':
        iflags = GDBM_NEWDB;
        break;
    default:
        PyErr_SetString(DbmError,
466
                        "First flag must be one of 'r', 'w', 'c' or 'n'");
Fred Drake's avatar
Fred Drake committed
467 468
        return NULL;
    }
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
    for (flags++; *flags != '\0'; flags++) {
        char buf[40];
        switch (*flags) {
#ifdef GDBM_FAST
            case 'f':
                iflags |= GDBM_FAST;
                break;
#endif
#ifdef GDBM_SYNC
            case 's':
                iflags |= GDBM_SYNC;
                break;
#endif
#ifdef GDBM_NOLOCK
            case 'u':
                iflags |= GDBM_NOLOCK;
                break;
#endif
            default:
488
                PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.",
489
                              *flags);
490 491 492 493 494
                PyErr_SetString(DbmError, buf);
                return NULL;
        }
    }

Fred Drake's avatar
Fred Drake committed
495
    return newdbmobject(name, iflags, mode);
496 497
}

498 499 500 501 502 503 504 505 506 507 508 509
static char dbmmodule_open_flags[] = "rwcn"
#ifdef GDBM_FAST
                                     "f"
#endif
#ifdef GDBM_SYNC
                                     "s"
#endif
#ifdef GDBM_NOLOCK
                                     "u"
#endif
                                     ;

510
static PyMethodDef dbmmodule_methods[] = {
Fred Drake's avatar
Fred Drake committed
511 512
    { "open", (PyCFunction)dbmopen, METH_VARARGS, dbmopen__doc__},
    { 0, 0 },
513 514
};

515 516

static struct PyModuleDef _gdbmmodule = {
517 518 519 520 521 522 523 524 525
        PyModuleDef_HEAD_INIT,
        "_gdbm",
        gdbmmodule__doc__,
        -1,
        dbmmodule_methods,
        NULL,
        NULL,
        NULL,
        NULL
526 527
};

528
PyMODINIT_FUNC
529
PyInit__gdbm(void) {
530
    PyObject *m, *d, *s;
Fred Drake's avatar
Fred Drake committed
531

532
    if (PyType_Ready(&Dbmtype) < 0)
533
            return NULL;
534
    m = PyModule_Create(&_gdbmmodule);
535
    if (m == NULL)
536
        return NULL;
Fred Drake's avatar
Fred Drake committed
537
    d = PyModule_GetDict(m);
538
    DbmError = PyErr_NewException("_gdbm.error", PyExc_IOError, NULL);
539
    if (DbmError != NULL) {
Fred Drake's avatar
Fred Drake committed
540
        PyDict_SetItemString(d, "error", DbmError);
541
        s = PyUnicode_FromString(dbmmodule_open_flags);
542 543
        PyDict_SetItemString(d, "open_flags", s);
        Py_DECREF(s);
544
    }
545
    return m;
546
}