diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-06-04 17:29:20 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-10-01 16:52:24 -0400 |
| commit | cc718cccc0bf8a01abdf4068c7ea4f32c9322af6 (patch) | |
| tree | e839526dd0ab64bf0d8babe01006e03987403a66 /lib/sqlalchemy/cextension | |
| parent | a3c964203e61f8deeb559b15a78cc640dee67012 (diff) | |
| download | sqlalchemy-cc718cccc0bf8a01abdf4068c7ea4f32c9322af6.tar.gz | |
Run row value processors up front
as part of a larger series of changes to generalize row-tuples,
RowProxy becomes plain Row and is no longer a "proxy"; the
DBAPI row is now copied directly into the Row when constructed,
result handling occurs at once.
Subsequent changes will break out Row into a new version that
behaves fully a tuple.
Change-Id: I2ffa156afce5d21c38f28e54c3a531f361345dd5
Diffstat (limited to 'lib/sqlalchemy/cextension')
| -rw-r--r-- | lib/sqlalchemy/cextension/resultproxy.c | 689 |
1 files changed, 426 insertions, 263 deletions
diff --git a/lib/sqlalchemy/cextension/resultproxy.c b/lib/sqlalchemy/cextension/resultproxy.c index c80445930..a94af1b2f 100644 --- a/lib/sqlalchemy/cextension/resultproxy.c +++ b/lib/sqlalchemy/cextension/resultproxy.c @@ -30,24 +30,23 @@ typedef struct { PyObject_HEAD PyObject *parent; PyObject *row; - PyObject *processors; PyObject *keymap; -} BaseRowProxy; +} BaseRow; /**************** - * BaseRowProxy * + * BaseRow * ****************/ static PyObject * safe_rowproxy_reconstructor(PyObject *self, PyObject *args) { PyObject *cls, *state, *tmp; - BaseRowProxy *obj; + BaseRow *obj; if (!PyArg_ParseTuple(args, "OO", &cls, &state)) return NULL; - obj = (BaseRowProxy *)PyObject_CallMethod(cls, "__new__", "O", cls); + obj = (BaseRow *)PyObject_CallMethod(cls, "__new__", "O", cls); if (obj == NULL) return NULL; @@ -59,10 +58,10 @@ safe_rowproxy_reconstructor(PyObject *self, PyObject *args) Py_DECREF(tmp); if (obj->parent == NULL || obj->row == NULL || - obj->processors == NULL || obj->keymap == NULL) { + obj->keymap == NULL) { PyErr_SetString(PyExc_RuntimeError, - "__setstate__ for BaseRowProxy subclasses must set values " - "for parent, row, processors and keymap"); + "__setstate__ for BaseRow subclasses must set values " + "for parent, row, and keymap"); Py_DECREF(obj); return NULL; } @@ -71,30 +70,64 @@ safe_rowproxy_reconstructor(PyObject *self, PyObject *args) } static int -BaseRowProxy_init(BaseRowProxy *self, PyObject *args, PyObject *kwds) +BaseRow_init(BaseRow *self, PyObject *args, PyObject *kwds) { - PyObject *parent, *row, *processors, *keymap; + PyObject *parent, *keymap, *row, *processors; + Py_ssize_t num_values, num_processors; + PyObject **valueptr, **funcptr, **resultptr; + PyObject *func, *result, *processed_value, *values_fastseq; - if (!PyArg_UnpackTuple(args, "BaseRowProxy", 4, 4, - &parent, &row, &processors, &keymap)) + if (!PyArg_UnpackTuple(args, "BaseRow", 4, 4, + &parent, &processors, &keymap, &row)) return -1; Py_INCREF(parent); self->parent = parent; - if (!PySequence_Check(row)) { - PyErr_SetString(PyExc_TypeError, "row must be a sequence"); + values_fastseq = PySequence_Fast(row, "row must be a sequence"); + if (values_fastseq == NULL) + return -1; + + num_values = PySequence_Length(values_fastseq); + num_processors = PyList_Size(processors); + if (num_values != num_processors) { + PyErr_Format(PyExc_RuntimeError, + "number of values in row (%d) differ from number of column " + "processors (%d)", + (int)num_values, (int)num_processors); return -1; } - Py_INCREF(row); - self->row = row; - if (!PyList_CheckExact(processors)) { - PyErr_SetString(PyExc_TypeError, "processors must be a list"); + result = PyTuple_New(num_values); + if (result == NULL) return -1; + + valueptr = PySequence_Fast_ITEMS(values_fastseq); + funcptr = PySequence_Fast_ITEMS(processors); + resultptr = PySequence_Fast_ITEMS(result); + while (--num_values >= 0) { + func = *funcptr; + if (func != Py_None) { + processed_value = PyObject_CallFunctionObjArgs( + func, *valueptr, NULL); + if (processed_value == NULL) { + Py_DECREF(values_fastseq); + Py_DECREF(result); + return -1; + } + *resultptr = processed_value; + } else { + Py_INCREF(*valueptr); + *resultptr = *valueptr; + } + valueptr++; + funcptr++; + resultptr++; } - Py_INCREF(processors); - self->processors = processors; + + Py_DECREF(values_fastseq); + + self->row = result; if (!PyDict_CheckExact(keymap)) { PyErr_SetString(PyExc_TypeError, "keymap must be a dict"); @@ -108,10 +141,10 @@ BaseRowProxy_init(BaseRowProxy *self, PyObject *args, PyObject *kwds) /* We need the reduce method because otherwise the default implementation * does very weird stuff for pickle protocol 0 and 1. It calls - * BaseRowProxy.__new__(RowProxy_instance) upon *pickling*. + * BaseRow.__new__(Row_instance) upon *pickling*. */ static PyObject * -BaseRowProxy_reduce(PyObject *self) +BaseRow_reduce(PyObject *self) { PyObject *method, *state; PyObject *module, *reconstructor, *cls; @@ -147,11 +180,10 @@ BaseRowProxy_reduce(PyObject *self) } static void -BaseRowProxy_dealloc(BaseRowProxy *self) +BaseRow_dealloc(BaseRow *self) { Py_XDECREF(self->parent); Py_XDECREF(self->row); - Py_XDECREF(self->processors); Py_XDECREF(self->keymap); #if PY_MAJOR_VERSION >= 3 Py_TYPE(self)->tp_free((PyObject *)self); @@ -161,73 +193,39 @@ BaseRowProxy_dealloc(BaseRowProxy *self) } static PyObject * -BaseRowProxy_processvalues(PyObject *values, PyObject *processors, int astuple) +BaseRow_valuescollection(PyObject *values, int astuple) { - Py_ssize_t num_values, num_processors; - PyObject **valueptr, **funcptr, **resultptr; - PyObject *func, *result, *processed_value, *values_fastseq; - - num_values = PySequence_Length(values); - num_processors = PyList_Size(processors); - if (num_values != num_processors) { - PyErr_Format(PyExc_RuntimeError, - "number of values in row (%d) differ from number of column " - "processors (%d)", - (int)num_values, (int)num_processors); - return NULL; - } + PyObject *result; if (astuple) { - result = PyTuple_New(num_values); + result = PySequence_Tuple(values); } else { - result = PyList_New(num_values); + result = PySequence_List(values); } if (result == NULL) return NULL; - values_fastseq = PySequence_Fast(values, "row must be a sequence"); - if (values_fastseq == NULL) - return NULL; - - valueptr = PySequence_Fast_ITEMS(values_fastseq); - funcptr = PySequence_Fast_ITEMS(processors); - resultptr = PySequence_Fast_ITEMS(result); - while (--num_values >= 0) { - func = *funcptr; - if (func != Py_None) { - processed_value = PyObject_CallFunctionObjArgs(func, *valueptr, - NULL); - if (processed_value == NULL) { - Py_DECREF(values_fastseq); - Py_DECREF(result); - return NULL; - } - *resultptr = processed_value; - } else { - Py_INCREF(*valueptr); - *resultptr = *valueptr; - } - valueptr++; - funcptr++; - resultptr++; - } - Py_DECREF(values_fastseq); return result; } static PyListObject * -BaseRowProxy_values(BaseRowProxy *self) +BaseRow_values_impl(BaseRow *self) +{ + return (PyListObject *)BaseRow_valuescollection(self->row, 0); +} + +static Py_hash_t +BaseRow_hash(BaseRow *self) { - return (PyListObject *)BaseRowProxy_processvalues(self->row, - self->processors, 0); + return PyObject_Hash(self->row); } static PyObject * -BaseRowProxy_iter(BaseRowProxy *self) +BaseRow_iter(BaseRow *self) { PyObject *values, *result; - values = BaseRowProxy_processvalues(self->row, self->processors, 1); + values = BaseRow_valuescollection(self->row, 1); if (values == NULL) return NULL; @@ -240,17 +238,34 @@ BaseRowProxy_iter(BaseRowProxy *self) } static Py_ssize_t -BaseRowProxy_length(BaseRowProxy *self) +BaseRow_length(BaseRow *self) { return PySequence_Length(self->row); } static PyObject * -BaseRowProxy_subscript(BaseRowProxy *self, PyObject *key) +BaseRow_getitem(BaseRow *self, Py_ssize_t i) +{ + PyObject *value; + PyObject *row; + + row = self->row; + + // row is a Tuple + value = PyTuple_GetItem(row, i); + + if (value == NULL) + return NULL; + + Py_INCREF(value); + + return value; +} + +static PyObject * +BaseRow_getitem_by_object(BaseRow *self, PyObject *key) { - PyObject *processors, *values; - PyObject *processor, *value, *processed_value; - PyObject *row, *record, *result, *indexobject; + PyObject *record, *indexobject; PyObject *exc_module, *exception, *cstr_obj; #if PY_MAJOR_VERSION >= 3 PyObject *bytes; @@ -258,158 +273,148 @@ BaseRowProxy_subscript(BaseRowProxy *self, PyObject *key) char *cstr_key; long index; int key_fallback = 0; - int tuple_check = 0; -#if PY_MAJOR_VERSION < 3 - if (PyInt_CheckExact(key)) { - index = PyInt_AS_LONG(key); - if (index < 0) - index += BaseRowProxy_length(self); - } else -#endif + // if record is non null, it's a borrowed reference + record = PyDict_GetItem((PyObject *)self->keymap, key); - if (PyLong_CheckExact(key)) { - index = PyLong_AsLong(key); - if ((index == -1) && PyErr_Occurred()) - /* -1 can be either the actual value, or an error flag. */ - return NULL; - if (index < 0) - index += BaseRowProxy_length(self); - } else if (PySlice_Check(key)) { - values = PyObject_GetItem(self->row, key); - if (values == NULL) + if (record == NULL) { + record = PyObject_CallMethod(self->parent, "_key_fallback", + "O", key); + if (record == NULL) return NULL; + key_fallback = 1; // boolean to indicate record is a new reference + } - processors = PyObject_GetItem(self->processors, key); - if (processors == NULL) { - Py_DECREF(values); - return NULL; - } + indexobject = PyTuple_GetItem(record, 0); + if (indexobject == NULL) + return NULL; - result = BaseRowProxy_processvalues(values, processors, 1); - Py_DECREF(values); - Py_DECREF(processors); - return result; - } else { - record = PyDict_GetItem((PyObject *)self->keymap, key); - if (record == NULL) { - record = PyObject_CallMethod(self->parent, "_key_fallback", - "O", key); - if (record == NULL) - return NULL; - key_fallback = 1; - } + if (key_fallback) { + Py_DECREF(record); + } - indexobject = PyTuple_GetItem(record, 2); - if (indexobject == NULL) + if (indexobject == Py_None) { + exc_module = PyImport_ImportModule("sqlalchemy.exc"); + if (exc_module == NULL) return NULL; - if (key_fallback) { - Py_DECREF(record); - } - - if (indexobject == Py_None) { - exc_module = PyImport_ImportModule("sqlalchemy.exc"); - if (exc_module == NULL) - return NULL; - - exception = PyObject_GetAttrString(exc_module, - "InvalidRequestError"); - Py_DECREF(exc_module); - if (exception == NULL) - return NULL; + exception = PyObject_GetAttrString(exc_module, + "InvalidRequestError"); + Py_DECREF(exc_module); + if (exception == NULL) + return NULL; - cstr_obj = PyTuple_GetItem(record, 1); - if (cstr_obj == NULL) - return NULL; + cstr_obj = PyTuple_GetItem(record, 2); + if (cstr_obj == NULL) + return NULL; - cstr_obj = PyObject_Str(cstr_obj); - if (cstr_obj == NULL) - return NULL; + cstr_obj = PyObject_Str(cstr_obj); + if (cstr_obj == NULL) + return NULL; /* - FIXME: raise encoding error exception (in both versions below) - if the key contains non-ascii chars, instead of an - InvalidRequestError without any message like in the - python version. + FIXME: raise encoding error exception (in both versions below) + if the key contains non-ascii chars, instead of an + InvalidRequestError without any message like in the + python version. */ #if PY_MAJOR_VERSION >= 3 - bytes = PyUnicode_AsASCIIString(cstr_obj); - if (bytes == NULL) - return NULL; - cstr_key = PyBytes_AS_STRING(bytes); + bytes = PyUnicode_AsASCIIString(cstr_obj); + if (bytes == NULL) + return NULL; + cstr_key = PyBytes_AS_STRING(bytes); #else - cstr_key = PyString_AsString(cstr_obj); + cstr_key = PyString_AsString(cstr_obj); #endif - if (cstr_key == NULL) { - Py_DECREF(cstr_obj); - return NULL; - } + if (cstr_key == NULL) { Py_DECREF(cstr_obj); - - PyErr_Format(exception, - "Ambiguous column name '%.200s' in " - "result set column descriptions", cstr_key); return NULL; } + Py_DECREF(cstr_obj); + + PyErr_Format(exception, + "Ambiguous column name '%.200s' in " + "result set column descriptions", cstr_key); + return NULL; + } #if PY_MAJOR_VERSION >= 3 - index = PyLong_AsLong(indexobject); + index = PyLong_AsLong(indexobject); #else - index = PyInt_AsLong(indexobject); + index = PyInt_AsLong(indexobject); #endif - if ((index == -1) && PyErr_Occurred()) - /* -1 can be either the actual value, or an error flag. */ - return NULL; - } - processor = PyList_GetItem(self->processors, index); - if (processor == NULL) + if ((index == -1) && PyErr_Occurred()) + /* -1 can be either the actual value, or an error flag. */ return NULL; - row = self->row; - if (PyTuple_CheckExact(row)) { - value = PyTuple_GetItem(row, index); - tuple_check = 1; - } - else { - value = PySequence_GetItem(row, index); - tuple_check = 0; - } + return BaseRow_getitem(self, index); - if (value == NULL) - return NULL; +} - if (processor != Py_None) { - processed_value = PyObject_CallFunctionObjArgs(processor, value, NULL); - if (!tuple_check) { - Py_DECREF(value); - } - return processed_value; +static PyObject * +BaseRow_subscript_impl(BaseRow *self, PyObject *key, int asmapping) +{ + PyObject *values; + PyObject *result; + long index; + +#if PY_MAJOR_VERSION < 3 + if (PyInt_CheckExact(key)) { + index = PyInt_AS_LONG(key); + if (index < 0) + index += BaseRow_length(self); + return BaseRow_getitem(self, index); + } else +#endif + + if (PyLong_CheckExact(key)) { + index = PyLong_AsLong(key); + if ((index == -1) && PyErr_Occurred()) + /* -1 can be either the actual value, or an error flag. */ + return NULL; + if (index < 0) + index += BaseRow_length(self); + return BaseRow_getitem(self, index); + } else if (PySlice_Check(key)) { + values = PyObject_GetItem(self->row, key); + if (values == NULL) + return NULL; + + result = BaseRow_valuescollection(values, 1); + Py_DECREF(values); + return result; } else { - if (tuple_check) { - Py_INCREF(value); - } - return value; + /* + // if we want to warn for non-integer access by getitem, + // that would happen here. + if (!asmapping) { + tmp = PyObject_CallMethod(self->parent, "_warn_for_nonint", ""); + if (tmp == NULL) { + return NULL; + } + Py_DECREF(tmp); + }*/ + return BaseRow_getitem_by_object(self, key); } } static PyObject * -BaseRowProxy_getitem(PyObject *self, Py_ssize_t i) +BaseRow_subscript(BaseRow *self, PyObject *key) { - PyObject *index; + return BaseRow_subscript_impl(self, key, 0); +} -#if PY_MAJOR_VERSION >= 3 - index = PyLong_FromSsize_t(i); -#else - index = PyInt_FromSsize_t(i); -#endif - return BaseRowProxy_subscript((BaseRowProxy*)self, index); +static PyObject * +BaseRow_subscript_mapping(BaseRow *self, PyObject *key) +{ + return BaseRow_subscript_impl(self, key, 1); } + static PyObject * -BaseRowProxy_getattro(BaseRowProxy *self, PyObject *name) +BaseRow_getattro(BaseRow *self, PyObject *name) { PyObject *tmp; #if PY_MAJOR_VERSION >= 3 @@ -424,7 +429,7 @@ BaseRowProxy_getattro(BaseRowProxy *self, PyObject *name) else return tmp; - tmp = BaseRowProxy_subscript(self, name); + tmp = BaseRow_subscript_mapping(self, name); if (tmp == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { #if PY_MAJOR_VERSION >= 3 @@ -453,14 +458,14 @@ BaseRowProxy_getattro(BaseRowProxy *self, PyObject *name) ***********************/ static PyObject * -BaseRowProxy_getparent(BaseRowProxy *self, void *closure) +BaseRow_getparent(BaseRow *self, void *closure) { Py_INCREF(self->parent); return self->parent; } static int -BaseRowProxy_setparent(BaseRowProxy *self, PyObject *value, void *closure) +BaseRow_setparent(BaseRow *self, PyObject *value, void *closure) { PyObject *module, *cls; @@ -494,14 +499,14 @@ BaseRowProxy_setparent(BaseRowProxy *self, PyObject *value, void *closure) } static PyObject * -BaseRowProxy_getrow(BaseRowProxy *self, void *closure) +BaseRow_getrow(BaseRow *self, void *closure) { Py_INCREF(self->row); return self->row; } static int -BaseRowProxy_setrow(BaseRowProxy *self, PyObject *value, void *closure) +BaseRow_setrow(BaseRow *self, PyObject *value, void *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, @@ -522,44 +527,17 @@ BaseRowProxy_setrow(BaseRowProxy *self, PyObject *value, void *closure) return 0; } -static PyObject * -BaseRowProxy_getprocessors(BaseRowProxy *self, void *closure) -{ - Py_INCREF(self->processors); - return self->processors; -} - -static int -BaseRowProxy_setprocessors(BaseRowProxy *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, - "Cannot delete the 'processors' attribute"); - return -1; - } - - if (!PyList_CheckExact(value)) { - PyErr_SetString(PyExc_TypeError, - "The 'processors' attribute value must be a list"); - return -1; - } - - Py_XDECREF(self->processors); - Py_INCREF(value); - self->processors = value; - return 0; -} static PyObject * -BaseRowProxy_getkeymap(BaseRowProxy *self, void *closure) +BaseRow_getkeymap(BaseRow *self, void *closure) { Py_INCREF(self->keymap); return self->keymap; } static int -BaseRowProxy_setkeymap(BaseRowProxy *self, PyObject *value, void *closure) +BaseRow_setkeymap(BaseRow *self, PyObject *value, void *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, @@ -580,39 +558,39 @@ BaseRowProxy_setkeymap(BaseRowProxy *self, PyObject *value, void *closure) return 0; } -static PyGetSetDef BaseRowProxy_getseters[] = { +static PyGetSetDef BaseRow_getseters[] = { {"_parent", - (getter)BaseRowProxy_getparent, (setter)BaseRowProxy_setparent, + (getter)BaseRow_getparent, (setter)BaseRow_setparent, "ResultMetaData", NULL}, - {"_row", - (getter)BaseRowProxy_getrow, (setter)BaseRowProxy_setrow, - "Original row tuple", - NULL}, - {"_processors", - (getter)BaseRowProxy_getprocessors, (setter)BaseRowProxy_setprocessors, - "list of type processors", + {"_data", + (getter)BaseRow_getrow, (setter)BaseRow_setrow, + "processed data list", NULL}, {"_keymap", - (getter)BaseRowProxy_getkeymap, (setter)BaseRowProxy_setkeymap, - "Key to (processor, index) dict", + (getter)BaseRow_getkeymap, (setter)BaseRow_setkeymap, + "Key to (obj, index) dict", NULL}, {NULL} }; -static PyMethodDef BaseRowProxy_methods[] = { - {"values", (PyCFunction)BaseRowProxy_values, METH_NOARGS, - "Return the values represented by this BaseRowProxy as a list."}, - {"__reduce__", (PyCFunction)BaseRowProxy_reduce, METH_NOARGS, +static PyMethodDef BaseRow_methods[] = { + {"_values_impl", (PyCFunction)BaseRow_values_impl, METH_NOARGS, + "Return the values represented by this BaseRow as a list."}, + {"__reduce__", (PyCFunction)BaseRow_reduce, METH_NOARGS, "Pickle support method."}, + {"_get_by_key_impl", (PyCFunction)BaseRow_subscript, METH_O, + "implement mapping-like getitem as well as sequence getitem"}, + {"_get_by_key_impl_mapping", (PyCFunction)BaseRow_subscript_mapping, METH_O, + "implement mapping-like getitem as well as sequence getitem"}, {NULL} /* Sentinel */ }; -static PySequenceMethods BaseRowProxy_as_sequence = { - (lenfunc)BaseRowProxy_length, /* sq_length */ +static PySequenceMethods BaseRow_as_sequence = { + (lenfunc)BaseRow_length, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ - (ssizeargfunc)BaseRowProxy_getitem, /* sq_item */ + (ssizeargfunc)BaseRow_getitem, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ @@ -621,56 +599,235 @@ static PySequenceMethods BaseRowProxy_as_sequence = { 0, /* sq_inplace_repeat */ }; -static PyMappingMethods BaseRowProxy_as_mapping = { - (lenfunc)BaseRowProxy_length, /* mp_length */ - (binaryfunc)BaseRowProxy_subscript, /* mp_subscript */ +static PyMappingMethods BaseRow_as_mapping = { + (lenfunc)BaseRow_length, /* mp_length */ + (binaryfunc)BaseRow_subscript_mapping, /* mp_subscript */ 0 /* mp_ass_subscript */ }; -static PyTypeObject BaseRowProxyType = { +static PyTypeObject BaseRowType = { PyVarObject_HEAD_INIT(NULL, 0) - "sqlalchemy.cresultproxy.BaseRowProxy", /* tp_name */ - sizeof(BaseRowProxy), /* tp_basicsize */ + "sqlalchemy.cresultproxy.BaseRow", /* tp_name */ + sizeof(BaseRow), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)BaseRowProxy_dealloc, /* tp_dealloc */ + (destructor)BaseRow_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ - &BaseRowProxy_as_sequence, /* tp_as_sequence */ - &BaseRowProxy_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ + &BaseRow_as_sequence, /* tp_as_sequence */ + &BaseRow_as_mapping, /* tp_as_mapping */ + (hashfunc)BaseRow_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - (getattrofunc)BaseRowProxy_getattro,/* tp_getattro */ + (getattrofunc)BaseRow_getattro,/* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "BaseRowProxy is a abstract base class for RowProxy", /* tp_doc */ + "BaseRow is a abstract base class for Row", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - (getiterfunc)BaseRowProxy_iter, /* tp_iter */ + (getiterfunc)BaseRow_iter, /* tp_iter */ 0, /* tp_iternext */ - BaseRowProxy_methods, /* tp_methods */ + BaseRow_methods, /* tp_methods */ 0, /* tp_members */ - BaseRowProxy_getseters, /* tp_getset */ + BaseRow_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)BaseRowProxy_init, /* tp_init */ + (initproc)BaseRow_init, /* tp_init */ 0, /* tp_alloc */ 0 /* tp_new */ }; + + +/* _tuplegetter function ************************************************/ +/* +retrieves segments of a row as tuples. + +mostly like operator.itemgetter but calls a fixed method instead, +returns tuple every time. + +*/ + +typedef struct { + PyObject_HEAD + Py_ssize_t nitems; + PyObject *item; +} tuplegetterobject; + +static PyTypeObject tuplegetter_type; + +static PyObject * +tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + tuplegetterobject *tg; + PyObject *item; + Py_ssize_t nitems; + + if (!_PyArg_NoKeywords("tuplegetter", kwds)) + return NULL; + + nitems = PyTuple_GET_SIZE(args); + item = args; + + tg = PyObject_GC_New(tuplegetterobject, &tuplegetter_type); + if (tg == NULL) + return NULL; + + Py_INCREF(item); + tg->item = item; + tg->nitems = nitems; + PyObject_GC_Track(tg); + return (PyObject *)tg; +} + +static void +tuplegetter_dealloc(tuplegetterobject *tg) +{ + PyObject_GC_UnTrack(tg); + Py_XDECREF(tg->item); + PyObject_GC_Del(tg); +} + +static int +tuplegetter_traverse(tuplegetterobject *tg, visitproc visit, void *arg) +{ + Py_VISIT(tg->item); + return 0; +} + +static PyObject * +tuplegetter_call(tuplegetterobject *tg, PyObject *args, PyObject *kw) +{ + PyObject *row, *result; + Py_ssize_t i, nitems=tg->nitems; + + assert(PyTuple_CheckExact(args)); + + // this is normally a BaseRow subclass but we are not doing + // strict checking at the moment + row = PyTuple_GET_ITEM(args, 0); + + assert(PyTuple_Check(tg->item)); + assert(PyTuple_GET_SIZE(tg->item) == nitems); + + result = PyTuple_New(nitems); + if (result == NULL) + return NULL; + + for (i=0 ; i < nitems ; i++) { + PyObject *item, *val; + item = PyTuple_GET_ITEM(tg->item, i); + + val = PyObject_CallMethod(row, "_get_by_key_impl_mapping", "O", item); + + // generic itemgetter version; if BaseRow __getitem__ is implemented + // in C directly then we can use that + //val = PyObject_GetItem(row, item); + if (val == NULL) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, i, val); + } + return result; +} + +static PyObject * +tuplegetter_repr(tuplegetterobject *tg) +{ + PyObject *repr; + const char *reprfmt; + + int status = Py_ReprEnter((PyObject *)tg); + if (status != 0) { + if (status < 0) + return NULL; + return PyUnicode_FromFormat("%s(...)", Py_TYPE(tg)->tp_name); + } + + reprfmt = tg->nitems == 1 ? "%s(%R)" : "%s%R"; + repr = PyUnicode_FromFormat(reprfmt, Py_TYPE(tg)->tp_name, tg->item); + Py_ReprLeave((PyObject *)tg); + return repr; +} + +static PyObject * +tuplegetter_reduce(tuplegetterobject *tg, PyObject *Py_UNUSED(ignored)) +{ + return PyTuple_Pack(2, Py_TYPE(tg), tg->item); +} + +PyDoc_STRVAR(reduce_doc, "Return state information for pickling"); + +static PyMethodDef tuplegetter_methods[] = { + {"__reduce__", (PyCFunction)tuplegetter_reduce, METH_NOARGS, + reduce_doc}, + {NULL} +}; + +PyDoc_STRVAR(tuplegetter_doc, +"tuplegetter(item, ...) --> tuplegetter object\n\ +\n\ +Return a callable object that fetches the given item(s) from its operand\n\ +and returns them as a tuple.\n"); + +static PyTypeObject tuplegetter_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "sqlalchemy.engine.util..tuplegetter", /* tp_name */ + sizeof(tuplegetterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)tuplegetter_dealloc, /* tp_dealloc */ + 0, /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + (reprfunc)tuplegetter_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)tuplegetter_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + tuplegetter_doc, /* tp_doc */ + (traverseproc)tuplegetter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + tuplegetter_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 */ + 0, /* tp_init */ + 0, /* tp_alloc */ + tuplegetter_new, /* tp_new */ + 0, /* tp_free */ +}; + + + static PyMethodDef module_methods[] = { {"safe_rowproxy_reconstructor", safe_rowproxy_reconstructor, METH_VARARGS, - "reconstruct a RowProxy instance from its pickled form."}, + "reconstruct a Row instance from its pickled form."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -706,10 +863,13 @@ initcresultproxy(void) { PyObject *m; - BaseRowProxyType.tp_new = PyType_GenericNew; - if (PyType_Ready(&BaseRowProxyType) < 0) + BaseRowType.tp_new = PyType_GenericNew; + if (PyType_Ready(&BaseRowType) < 0) INITERROR; + if (PyType_Ready(&tuplegetter_type) < 0) + return NULL; + #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&module_def); #else @@ -718,8 +878,11 @@ initcresultproxy(void) if (m == NULL) INITERROR; - Py_INCREF(&BaseRowProxyType); - PyModule_AddObject(m, "BaseRowProxy", (PyObject *)&BaseRowProxyType); + Py_INCREF(&BaseRowType); + PyModule_AddObject(m, "BaseRow", (PyObject *)&BaseRowType); + + Py_INCREF(&tuplegetter_type); + PyModule_AddObject(m, "tuplegetter", (PyObject *)&tuplegetter_type); #if PY_MAJOR_VERSION >= 3 return m; |
