Re: row_factory and DictCursor
Joel Nothman <[email protected]>
| Newsgroups | gmane.comp.python.db.psycopg.devel |
|---|---|
| Message-ID | <[email protected]> |
Forgot to attach a diff, didn't I? Here I've cleaned up extras.py some more, but no changes affect backwards compatibility. Note the diff to cursor_type.c assumes my column-name patch. - Joel On Thu, Jun 11, 2009 at 11:08 PM, Joel Nothman <[email protected] > wrote: > I've implemented the initial change, which passes the constructor of the > row_factory a tuple of data, instead of setting each element in the > constructed object. > _______________________________________________ Psycopg mailing list Psycopg-IAPFreCvJWPBWskQ1e/[email protected] http://lists.initd.org/mailman/listinfo/psycopg
row_factory_fix.diff
(application/octet-stream, 9.9 KB)
diff --git a/lib/extras.py b/lib/extras.py
index e466570..874137f 100644
--- a/lib/extras.py
+++ b/lib/extras.py
@@ -33,6 +33,8 @@ from psycopg2.extensions import connection as _connection
from psycopg2.extensions import adapt as _A
+##### DICT CURSORS
+
class DictCursorBase(_cursor):
"""Base class for all dict-like cursors."""
@@ -44,57 +46,30 @@ class DictCursorBase(_cursor):
raise NotImplementedError(
"DictCursorBase can't be instantiated without a row factory.")
_cursor.__init__(self, *args, **kwargs)
- self._query_executed = 0
- self._prefetch = 0
self.row_factory = row_factory
- def fetchone(self):
- if self._prefetch:
- res = _cursor.fetchone(self)
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = _cursor.fetchone(self)
- return res
+ def execute(self, *args, **kwargs):
+ self._reset_index()
+ return _cursor.execute(self, *args, **kwargs)
- def fetchmany(self, size=None):
- if self._prefetch:
- res = _cursor.fetchmany(self, size)
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = _cursor.fetchmany(self, size)
- return res
+ def callproc(self, *args, **kwargs):
+ self._reset_index()
+ return _cursor.callproc(self, *args, **kwargs)
- def fetchall(self):
- if self._prefetch:
- res = _cursor.fetchall(self)
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = _cursor.fetchall(self)
- return res
+ def _reset_index(self):
+ raise NotImplementedError("_reset_index() needs to be implemented by a child class")
- def next(self):
- if self._prefetch:
- res = _cursor.fetchone(self)
- if res is None:
- raise StopIteration()
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = _cursor.fetchone(self)
- if res is None:
- raise StopIteration()
- return res
-class DictConnection(_connection):
- """A connection that uses DictCursor automatically."""
- def cursor(self, name=None):
- if name is None:
+def connection_for_cursor(curs_cls, cls_name='Connection'):
+ class Connection(_connection):
+ def cursor(self, name=None):
+ if name:
+ return _connection.cursor(self, name=name, cursor_factory=DictCursor)
return _connection.cursor(self, cursor_factory=DictCursor)
- else:
- return _connection.cursor(self, name, cursor_factory=DictCursor)
+
+ Connection.__name__ = cls_name
+ return Connection
+
class DictCursor(DictCursorBase):
"""A cursor that keeps a list of column name -> index mappings."""
@@ -102,32 +77,27 @@ class DictCursor(DictCursorBase):
def __init__(self, *args, **kwargs):
kwargs['row_factory'] = DictRow
DictCursorBase.__init__(self, *args, **kwargs)
- self._prefetch = 1
-
- def execute(self, query, vars=None, async=0):
- self.index = {}
- self._query_executed = 1
- return _cursor.execute(self, query, vars, async)
- def callproc(self, procname, vars=None):
+ def _reset_index(self):
self.index = {}
- self._query_executed = 1
- return _cursor.callproc(self, procname, vars)
def _build_index(self):
- if self._query_executed == 1 and self.description:
- for i in range(len(self.description)):
- self.index[self.description[i][0]] = i
- self._query_executed = 0
+ if self.index:
+ return
+ for i, d in enumerate(self.description):
+ self.index[d[0]] = i
+
+DictConnection = connection_for_cursor(DictCursor, 'DictConnection')
class DictRow(list):
"""A row object that allow by-colun-name access to data."""
__slots__ = ('_index',)
- def __init__(self, cursor):
+ def __init__(self, cursor, data):
+ cursor._build_index()
self._index = cursor.index
- self[:] = [None] * len(cursor.description)
+ self[:] = data
def __getitem__(self, x):
if type(x) != int:
@@ -171,13 +141,6 @@ class DictRow(list):
def __contains__(self, x):
return self._index.__contains__(x)
-class RealDictConnection(_connection):
- """A connection that uses RealDictCursor automatically."""
- def cursor(self, name=None):
- if name is None:
- return _connection.cursor(self, cursor_factory=RealDictCursor)
- else:
- return _connection.cursor(self, name, cursor_factory=RealDictCursor)
class RealDictCursor(DictCursorBase):
"""A cursor that uses a real dict as the base type for rows.
@@ -191,31 +154,27 @@ class RealDictCursor(DictCursorBase):
def __init__(self, *args, **kwargs):
kwargs['row_factory'] = RealDictRow
DictCursorBase.__init__(self, *args, **kwargs)
- self._prefetch = 0
- def execute(self, query, vars=None, async=0):
- self.column_mapping = []
- self._query_executed = 1
- return _cursor.execute(self, query, vars, async)
-
- def callproc(self, procname, vars=None):
- self.column_mapping = []
- self._query_executed = 1
- return _cursor.callproc(self, procname, vars)
+ def _reset_index(self):
+ self.column_mapping = {}
def _build_index(self):
- if self._query_executed == 1 and self.description:
- for i in range(len(self.description)):
- self.column_mapping.append(self.description[i][0])
- self._query_executed = 0
+ if self.column_mapping:
+ return
+ for d in self.description:
+ self.column_mapping.append(d[0])
-class RealDictRow(dict):
+RealDictConnection = connection_for_cursor(RealDictCursor, 'RealDictConnection')
+class RealDictRow(dict):
__slots__ = ('_column_mapping')
- def __init__(self, cursor):
+ def __init__(self, cursor, data):
dict.__init__(self)
+ cursor._build_index()
self._column_mapping = cursor.column_mapping
+ for i, d in enumerate(data):
+ self[i] = d
def __setitem__(self, name, value):
if type(name) == int:
@@ -223,6 +182,8 @@ class RealDictRow(dict):
return dict.__setitem__(self, name, value)
+##### LOGGING CONNECTIONS
+
class LoggingConnection(_connection):
"""A connection that logs all queries to a file or logger object."""
diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c
index 4294138..6509823 100644
--- a/psycopg/cursor_type.c
+++ b/psycopg/cursor_type.c
@@ -531,7 +531,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
}
else {
if (self->rowcount == -1)
- rowcount = -1;
+ rowcount = -1;
else if (rowcount >= 0)
rowcount += self->rowcount;
Py_DECREF(v);
@@ -682,7 +682,7 @@ _psyco_curs_prefetch(cursorObject *self)
static PyObject *
_psyco_curs_buildrow_fill(cursorObject *self, PyObject *res,
- int row, int n, int istuple)
+ int row, int n)
{
int i, len;
const char *str;
@@ -709,18 +709,7 @@ _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res,
FORMAT_CODE_PY_SSIZE_T,
val->ob_refcnt
);
- if (istuple) {
- PyTuple_SET_ITEM(res, i, val);
- }
- else {
- int err = PySequence_SetItem(res, i, val);
- Py_DECREF(val);
- if (err == -1) {
- Py_DECREF(res);
- res = NULL;
- break;
- }
- }
+ PyTuple_SET_ITEM(res, i, val);
}
else {
/* an error occurred in the type system, we return NULL to raise
@@ -738,22 +727,13 @@ static PyObject *
_psyco_curs_buildrow(cursorObject *self, int row)
{
int n;
-
- n = PQnfields(self->pgres);
- return _psyco_curs_buildrow_fill(self, PyTuple_New(n), row, n, 1);
-}
-
-static PyObject *
-_psyco_curs_buildrow_with_factory(cursorObject *self, int row)
-{
- int n;
PyObject *res;
n = PQnfields(self->pgres);
- if ((res = PyObject_CallFunction(self->tuple_factory, "O", self))== NULL)
- return NULL;
-
- return _psyco_curs_buildrow_fill(self, res, row, n, 0);
+ res = _psyco_curs_buildrow_fill(self, PyTuple_New(n), row, n);
+ if (self->tuple_factory != Py_None)
+ res = PyObject_CallFunction(self->tuple_factory, "OO", self, res);
+ return res;
}
static PyObject *
@@ -785,10 +765,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
return Py_None;
}
- if (self->tuple_factory == Py_None)
- res = _psyco_curs_buildrow(self, self->row);
- else
- res = _psyco_curs_buildrow_with_factory(self, self->row);
+ res = _psyco_curs_buildrow(self, self->row);
self->row++; /* move the counter to next line */
@@ -851,10 +828,7 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
list = PyList_New(size);
for (i = 0; i < size; i++) {
- if (self->tuple_factory == Py_None)
- res = _psyco_curs_buildrow(self, self->row);
- else
- res = _psyco_curs_buildrow_with_factory(self, self->row);
+ res = _psyco_curs_buildrow(self, self->row);
self->row++;
@@ -917,10 +891,7 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
list = PyList_New(size);
for (i = 0; i < size; i++) {
- if (self->tuple_factory == Py_None)
- res = _psyco_curs_buildrow(self, self->row);
- else
- res = _psyco_curs_buildrow_with_factory(self, self->row);
+ res = _psyco_curs_buildrow(self, self->row);
self->row++;