row.c 8.55 KB
Newer Older
1 2
/* row.c - an enhanced tuple for database rows
 *
3
 * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * This file is part of pysqlite.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

#include "row.h"
#include "cursor.h"
#include "sqlitecompat.h"

28
void pysqlite_row_dealloc(pysqlite_Row* self)
29 30 31 32
{
    Py_XDECREF(self->data);
    Py_XDECREF(self->description);

33
    Py_TYPE(self)->tp_free((PyObject*)self);
34 35
}

36
int pysqlite_row_init(pysqlite_Row* self, PyObject* args, PyObject* kwargs)
37 38
{
    PyObject* data;
39
    pysqlite_Cursor* cursor;
40 41 42 43 44 45 46 47

    self->data = 0;
    self->description = 0;

    if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) {
        return -1;
    }

48
    if (!PyObject_IsInstance((PyObject*)cursor, (PyObject*)&pysqlite_CursorType)) {
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
        PyErr_SetString(PyExc_TypeError, "instance of cursor required for first argument");
        return -1;
    }

    if (!PyTuple_Check(data)) {
        PyErr_SetString(PyExc_TypeError, "tuple required for second argument");
        return -1;
    }

    Py_INCREF(data);
    self->data = data;

    Py_INCREF(cursor->description);
    self->description = cursor->description;

    return 0;
}

67
PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx)
68 69 70 71 72 73 74 75 76 77 78
{
    long _idx;
    char* key;
    int nitems, i;
    char* compare_key;

    char* p1;
    char* p2;

    PyObject* item;

79
    if (PyLong_Check(idx)) {
80 81 82 83
        _idx = PyLong_AsLong(idx);
        item = PyTuple_GetItem(self->data, _idx);
        Py_XINCREF(item);
        return item;
84
    } else if (PyUnicode_Check(idx)) {
85
        key = _PyUnicode_AsString(idx);
86 87
        if (key == NULL)
            return NULL;
88 89 90 91

        nitems = PyTuple_Size(self->description);

        for (i = 0; i < nitems; i++) {
92
            compare_key = _PyUnicode_AsString(PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0));
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
            if (!compare_key) {
                return NULL;
            }

            p1 = key;
            p2 = compare_key;

            while (1) {
                if ((*p1 == (char)0) || (*p2 == (char)0)) {
                    break;
                }

                if ((*p1 | 0x20) != (*p2 | 0x20)) {
                    break;
                }

                p1++;
                p2++;
            }

            if ((*p1 == (char)0) && (*p2 == (char)0)) {
                /* found item */
                item = PyTuple_GetItem(self->data, i);
                Py_INCREF(item);
                return item;
            }

        }

        PyErr_SetString(PyExc_IndexError, "No item with that key");
        return NULL;
    } else if (PySlice_Check(idx)) {
        PyErr_SetString(PyExc_ValueError, "slices not implemented, yet");
        return NULL;
    } else {
        PyErr_SetString(PyExc_IndexError, "Index must be int or string");
        return NULL;
    }
}

133
Py_ssize_t pysqlite_row_length(pysqlite_Row* self, PyObject* args, PyObject* kwargs)
134 135 136 137
{
    return PyTuple_GET_SIZE(self->data);
}

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
PyObject* pysqlite_row_keys(pysqlite_Row* self, PyObject* args, PyObject* kwargs)
{
    PyObject* list;
    int nitems, i;

    list = PyList_New(0);
    if (!list) {
        return NULL;
    }
    nitems = PyTuple_Size(self->description);

    for (i = 0; i < nitems; i++) {
        if (PyList_Append(list, PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0)) != 0) {
            Py_DECREF(list);
            return NULL;
        }
    }

    return list;
}

159 160 161 162 163
static int pysqlite_row_print(pysqlite_Row* self, FILE *fp, int flags)
{
    return (&PyTuple_Type)->tp_print(self->data, fp, flags);
}

164 165 166 167
static PyObject* pysqlite_iter(pysqlite_Row* self)
{
    return PyObject_GetIter(self->data);
}
168

Georg Brandl's avatar
Georg Brandl committed
169
static Py_hash_t pysqlite_row_hash(pysqlite_Row *self)
Christian Heimes's avatar
Christian Heimes committed
170 171 172 173 174 175 176
{
    return PyObject_Hash(self->description) ^ PyObject_Hash(self->data);
}

static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, int opid)
{
    if (opid != Py_EQ && opid != Py_NE) {
177 178
        Py_INCREF(Py_NotImplemented);
        return Py_NotImplemented;
Christian Heimes's avatar
Christian Heimes committed
179 180
    }
    if (PyType_IsSubtype(Py_TYPE(_other), &pysqlite_RowType)) {
181 182 183 184 185 186 187
        pysqlite_Row *other = (pysqlite_Row *)_other;
        PyObject *res = PyObject_RichCompare(self->description, other->description, opid);
        if ((opid == Py_EQ && res == Py_True)
            || (opid == Py_NE && res == Py_False)) {
            Py_DECREF(res);
            return PyObject_RichCompare(self->data, other->data, opid);
        }
Christian Heimes's avatar
Christian Heimes committed
188 189 190 191 192
    }
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
}

193 194 195
PyMappingMethods pysqlite_row_as_mapping = {
    /* mp_length        */ (lenfunc)pysqlite_row_length,
    /* mp_subscript     */ (binaryfunc)pysqlite_row_subscript,
196 197 198
    /* mp_ass_subscript */ (objobjargproc)0,
};

199 200 201 202 203 204
static PyMethodDef pysqlite_row_methods[] = {
    {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS,
        PyDoc_STR("Returns the keys of the row.")},
    {NULL, NULL}
};

205

206
PyTypeObject pysqlite_RowType = {
207
        PyVarObject_HEAD_INIT(NULL, 0)
208
        MODULE_NAME ".Row",                             /* tp_name */
209
        sizeof(pysqlite_Row),                           /* tp_basicsize */
210
        0,                                              /* tp_itemsize */
211
        (destructor)pysqlite_row_dealloc,               /* tp_dealloc */
212
        (printfunc)pysqlite_row_print,                  /* tp_print */
213 214
        0,                                              /* tp_getattr */
        0,                                              /* tp_setattr */
215
        0,                                              /* tp_reserved */
216 217 218 219
        0,                                              /* tp_repr */
        0,                                              /* tp_as_number */
        0,                                              /* tp_as_sequence */
        0,                                              /* tp_as_mapping */
Christian Heimes's avatar
Christian Heimes committed
220
        (hashfunc)pysqlite_row_hash,                    /* tp_hash */
221 222 223 224 225 226 227
        0,                                              /* tp_call */
        0,                                              /* tp_str */
        0,                                              /* tp_getattro */
        0,                                              /* tp_setattro */
        0,                                              /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */
        0,                                              /* tp_doc */
228
        (traverseproc)0,                                /* tp_traverse */
229
        0,                                              /* tp_clear */
Christian Heimes's avatar
Christian Heimes committed
230
        (richcmpfunc)pysqlite_row_richcompare,          /* tp_richcompare */
231
        0,                                              /* tp_weaklistoffset */
232
        (getiterfunc)pysqlite_iter,                     /* tp_iter */
233
        0,                                              /* tp_iternext */
234
        pysqlite_row_methods,                           /* tp_methods */
235 236 237 238 239 240 241
        0,                                              /* tp_members */
        0,                                              /* tp_getset */
        0,                                              /* tp_base */
        0,                                              /* tp_dict */
        0,                                              /* tp_descr_get */
        0,                                              /* tp_descr_set */
        0,                                              /* tp_dictoffset */
242
        (initproc)pysqlite_row_init,                    /* tp_init */
243 244 245 246 247
        0,                                              /* tp_alloc */
        0,                                              /* tp_new */
        0                                               /* tp_free */
};

248
extern int pysqlite_row_setup_types(void)
249
{
250 251 252
    pysqlite_RowType.tp_new = PyType_GenericNew;
    pysqlite_RowType.tp_as_mapping = &pysqlite_row_as_mapping;
    return PyType_Ready(&pysqlite_RowType);
253
}