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++;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.