to [ticket:1892] as this was supposed
to be part of that, this is [ticket:2491].
+- engine
+ - [bug] Fixed memory leak in C version of
+ result proxy whereby DBAPIs which don't deliver
+ pure Python tuples for result rows would
+ fail to decrement refcounts correctly.
+ The most prominently affected DBAPI
+ is pyodbc. [ticket:2489]
+
- oracle
- [bug] Added ROWID to oracle.*, [ticket:2483]
BaseRowProxy_subscript(BaseRowProxy *self, PyObject *key)
{
PyObject *processors, *values;
- PyObject *processor, *value;
+ PyObject *processor, *value, *processed_value;
PyObject *row, *record, *result, *indexobject;
PyObject *exc_module, *exception;
char *cstr_key;
long index;
int key_fallback = 0;
-
+ int tuple_check = 0;
+
if (PyInt_CheckExact(key)) {
index = PyInt_AS_LONG(key);
} else if (PyLong_CheckExact(key)) {
return NULL;
row = self->row;
- if (PyTuple_CheckExact(row))
+ if (PyTuple_CheckExact(row)) {
value = PyTuple_GetItem(row, index);
- else
+ tuple_check = 1;
+ }
+ else {
value = PySequence_GetItem(row, index);
+ tuple_check = 0;
+ }
+
if (value == NULL)
return NULL;
if (processor != Py_None) {
- return PyObject_CallFunctionObjArgs(processor, value, NULL);
+ processed_value = PyObject_CallFunctionObjArgs(processor, value, NULL);
+ if (!tuple_check) {
+ Py_DECREF(value);
+ }
+ return processed_value;
} else {
- Py_INCREF(value);
+ if (tuple_check) {
+ Py_INCREF(value);
+ }
return value;
}
}
from sqlalchemy import *
from test.lib import *
+from test.lib.testing import eq_
NUM_FIELDS = 10
NUM_RECORDS = 1000
e.execute("select 1")
go()
+
+class RowProxyTest(fixtures.TestBase):
+ def _rowproxy_fixture(self, keys, processors, row):
+ from sqlalchemy.engine.base import RowProxy
+ class MockMeta(object):
+ def __init__(self):
+ pass
+
+ metadata = MockMeta()
+
+ keymap = {}
+ for index, (keyobjs, processor, values) in \
+ enumerate(zip(keys, processors, row)):
+ for key in keyobjs:
+ keymap[key] = (processor, key, index)
+ keymap[index] = (processor, key, index)
+ return RowProxy(metadata, row, processors, keymap)
+
+ def _test_getitem_value_refcounts(self, seq_factory):
+ import sys
+ col1, col2 = object(), object()
+ def proc1(value):
+ return value
+ value1, value2 = "x", "y"
+ row = self._rowproxy_fixture(
+ [(col1, "a"),(col2, "b")],
+ [proc1, None],
+ seq_factory([value1, value2])
+ )
+
+ v1_refcount = sys.getrefcount(value1)
+ v2_refcount = sys.getrefcount(value2)
+ for i in range(10):
+ row[col1]
+ row["a"]
+ row[col2]
+ row["b"]
+ row[0]
+ row[1]
+ row[0:2]
+ eq_(sys.getrefcount(value1), v1_refcount)
+ eq_(sys.getrefcount(value2), v2_refcount)
+
+ def test_value_refcounts_pure_tuple(self):
+ self._test_getitem_value_refcounts(tuple)
+
+ def test_value_refcounts_custom_seq(self):
+ class CustomSeq(object):
+ def __init__(self, data):
+ self.data = data
+
+ def __getitem__(self, item):
+ return self.data[item]
+
+ def __iter__(self):
+ return iter(self.data)
+ self._test_getitem_value_refcounts(CustomSeq)