/* row.c - an enhanced tuple for database rows | |
* | |
* Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> | |
* | |
* 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" | |
void pysqlite_row_dealloc(pysqlite_Row* self) | |
{ | |
Py_XDECREF(self->data); | |
Py_XDECREF(self->description); | |
Py_TYPE(self)->tp_free((PyObject*)self); | |
} | |
int pysqlite_row_init(pysqlite_Row* self, PyObject* args, PyObject* kwargs) | |
{ | |
PyObject* data; | |
pysqlite_Cursor* cursor; | |
self->data = 0; | |
self->description = 0; | |
if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) { | |
return -1; | |
} | |
if (!PyObject_IsInstance((PyObject*)cursor, (PyObject*)&pysqlite_CursorType)) { | |
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; | |
} | |
PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx) | |
{ | |
long _idx; | |
char* key; | |
int nitems, i; | |
char* compare_key; | |
char* p1; | |
char* p2; | |
PyObject* item; | |
if (PyInt_Check(idx)) { | |
_idx = PyInt_AsLong(idx); | |
item = PyTuple_GetItem(self->data, _idx); | |
Py_XINCREF(item); | |
return item; | |
} else if (PyLong_Check(idx)) { | |
_idx = PyLong_AsLong(idx); | |
item = PyTuple_GetItem(self->data, _idx); | |
Py_XINCREF(item); | |
return item; | |
} else if (PyString_Check(idx)) { | |
key = PyString_AsString(idx); | |
nitems = PyTuple_Size(self->description); | |
for (i = 0; i < nitems; i++) { | |
compare_key = PyString_AsString(PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0)); | |
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; | |
} | |
} | |
Py_ssize_t pysqlite_row_length(pysqlite_Row* self, PyObject* args, PyObject* kwargs) | |
{ | |
return PyTuple_GET_SIZE(self->data); | |
} | |
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; | |
} | |
static int pysqlite_row_print(pysqlite_Row* self, FILE *fp, int flags) | |
{ | |
return (&PyTuple_Type)->tp_print(self->data, fp, flags); | |
} | |
static PyObject* pysqlite_iter(pysqlite_Row* self) | |
{ | |
return PyObject_GetIter(self->data); | |
} | |
static long pysqlite_row_hash(pysqlite_Row *self) | |
{ | |
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) { | |
Py_INCREF(Py_NotImplemented); | |
return Py_NotImplemented; | |
} | |
if (PyType_IsSubtype(Py_TYPE(_other), &pysqlite_RowType)) { | |
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); | |
} | |
} | |
Py_INCREF(Py_NotImplemented); | |
return Py_NotImplemented; | |
} | |
PyMappingMethods pysqlite_row_as_mapping = { | |
/* mp_length */ (lenfunc)pysqlite_row_length, | |
/* mp_subscript */ (binaryfunc)pysqlite_row_subscript, | |
/* mp_ass_subscript */ (objobjargproc)0, | |
}; | |
static PyMethodDef pysqlite_row_methods[] = { | |
{"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, | |
PyDoc_STR("Returns the keys of the row.")}, | |
{NULL, NULL} | |
}; | |
PyTypeObject pysqlite_RowType = { | |
PyVarObject_HEAD_INIT(NULL, 0) | |
MODULE_NAME ".Row", /* tp_name */ | |
sizeof(pysqlite_Row), /* tp_basicsize */ | |
0, /* tp_itemsize */ | |
(destructor)pysqlite_row_dealloc, /* tp_dealloc */ | |
(printfunc)pysqlite_row_print, /* tp_print */ | |
0, /* tp_getattr */ | |
0, /* tp_setattr */ | |
0, /* tp_compare */ | |
0, /* tp_repr */ | |
0, /* tp_as_number */ | |
0, /* tp_as_sequence */ | |
0, /* tp_as_mapping */ | |
(hashfunc)pysqlite_row_hash, /* tp_hash */ | |
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 */ | |
(traverseproc)0, /* tp_traverse */ | |
0, /* tp_clear */ | |
(richcmpfunc)pysqlite_row_richcompare, /* tp_richcompare */ | |
0, /* tp_weaklistoffset */ | |
(getiterfunc)pysqlite_iter, /* tp_iter */ | |
0, /* tp_iternext */ | |
pysqlite_row_methods, /* tp_methods */ | |
0, /* tp_members */ | |
0, /* tp_getset */ | |
0, /* tp_base */ | |
0, /* tp_dict */ | |
0, /* tp_descr_get */ | |
0, /* tp_descr_set */ | |
0, /* tp_dictoffset */ | |
(initproc)pysqlite_row_init, /* tp_init */ | |
0, /* tp_alloc */ | |
0, /* tp_new */ | |
0 /* tp_free */ | |
}; | |
extern int pysqlite_row_setup_types(void) | |
{ | |
pysqlite_RowType.tp_new = PyType_GenericNew; | |
pysqlite_RowType.tp_as_mapping = &pysqlite_row_as_mapping; | |
return PyType_Ready(&pysqlite_RowType); | |
} |