summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/cextension
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2019-06-04 17:29:20 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2019-10-01 16:52:24 -0400
commitcc718cccc0bf8a01abdf4068c7ea4f32c9322af6 (patch)
treee839526dd0ab64bf0d8babe01006e03987403a66 /lib/sqlalchemy/cextension
parenta3c964203e61f8deeb559b15a78cc640dee67012 (diff)
downloadsqlalchemy-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.c689
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;