Added RealDictCursor from #143.

This commit is contained in:
Federico Di Gregorio 2007-01-16 13:45:41 +00:00
parent 5c425f5294
commit f43a52f781
3 changed files with 116 additions and 35 deletions

View File

@ -1,5 +1,10 @@
2007-01-16 Federico Di Gregorio <fog@initd.org> 2007-01-16 Federico Di Gregorio <fog@initd.org>
* lib/extras.py: merged DictCursor from #143 and renamed it RealDictCursor
because allows access by cursor keys _only_. Also cleaned up a little bit
the implementation of both DictCursor and RealDictCursor by introducing
DictCursorBase.
* psycopg/pqpath.cs: new checks for NULL values (meaning an * psycopg/pqpath.cs: new checks for NULL values (meaning an
exception was raised) in all method calls (fixes #134). exception was raised) in all method calls (fixes #134).

View File

@ -32,14 +32,34 @@ print "Encoding for this connection is", conn.encoding
curs = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) curs = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
curs.execute("SELECT 1 AS foo, 'cip' AS bar, date(now()) as zot") curs.execute("SELECT 1 AS foo, 'cip' AS bar, date(now()) as zot")
print "Cursor's row factory is", curs.row_factory
data = curs.fetchone() data = curs.fetchone()
print "The type of the data row is", type(data)
print "Some data accessed both as tuple and dict:" print "Some data accessed both as tuple and dict:"
print " ", data['foo'], data['bar'], data['zot'] print " ", data['foo'], data['bar'], data['zot']
print " ", data[0], data[1], data[2] print " ", data[0], data[1], data[2]
# execute another query and demostrate we can still access the row # execute another query and demostrate we can still access the row
curs.execute("SELECT 2 AS foo") curs.execute("SELECT 2 AS foo")
print "The type of the data row is", type(data)
print "Some more data accessed both as tuple and dict:" print "Some more data accessed both as tuple and dict:"
print " ", data['foo'], data['bar'], data['zot'] print " ", data['foo'], data['bar'], data['zot']
print " ", data[0], data[1], data[2] print " ", data[0], data[1], data[2]
curs = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
curs.execute("SELECT 1 AS foo, 'cip' AS bar, date(now()) as zot")
print "Cursor's row factory is", curs.row_factory
data = curs.fetchone()
print "The type of the data row is", type(data)
print "Some data accessed both as tuple and dict:"
print " ", data['foo'], data['bar'], data['zot']
print " ", "No access using indices: this is a specialized cursor."
# execute another query and demostrate we can still access the row
curs.execute("SELECT 2 AS foo")
print "The type of the data row is", type(data)
print "Some more data accessed both as tuple and dict:"
print " ", data['foo'], data['bar'], data['zot']
print " ", "No access using indices: this is a specialized cursor."

View File

@ -30,59 +30,70 @@ from psycopg2.extensions import connection as _connection
from psycopg2.extensions import adapt as _A from psycopg2.extensions import adapt as _A
class DictCursorBase(_cursor):
"""Base class for all dict-like cursors."""
def __init__(self, *args, **kwargs):
if kwargs.has_key('row_factory'):
row_factory = kwargs['row_factory']
del kwargs['row_factory']
else:
raise NotImplementedError(
"DictCursorBase can't be instantiated without a row factory.")
_cursor.__init__(self, *args, **kwargs)
self._query_executed = 0
self.row_factory = row_factory
def fetchone(self):
if self._query_executed:
self._build_index()
return _cursor.fetchone(self)
def fetchmany(self, size=None):
if self._query_executed:
self._build_index()
return _cursor.fetchmany(self, size)
def fetchall(self):
if self._query_executed:
self._build_index()
return _cursor.fetchall(self)
def next(self):
if self._query_executed:
self._build_index()
res = _cursor.fetchone(self)
if res is None:
raise StopIteration()
return res
class DictConnection(_connection): class DictConnection(_connection):
"""A connection that uses DictCursor automatically.""" """A connection that uses DictCursor automatically."""
def cursor(self): def cursor(self):
return _connection.cursor(self, cursor_factory=DictCursor) return _connection.cursor(self, cursor_factory=DictCursor)
class DictCursor(_cursor): class DictCursor(DictCursorBase):
"""A cursor that keeps a list of column name -> index mappings.""" """A cursor that keeps a list of column name -> index mappings."""
__query_executed = 0 def __init__(self, *args, **kwargs):
kwargs['row_factory'] = DictRow
DictCursorBase.__init__(self, *args, **kwargs)
def execute(self, query, vars=None, async=0): def execute(self, query, vars=None, async=0):
self.row_factory = DictRow
self.index = {} self.index = {}
self.__query_executed = 1 self._query_executed = 1
return _cursor.execute(self, query, vars, async) return _cursor.execute(self, query, vars, async)
def callproc(self, procname, vars=None): def callproc(self, procname, vars=None):
self.row_factory = DictRow
self.index = {} self.index = {}
self.__query_executed = 1 self._query_executed = 1
return _cursor.callproc(self, procname, vars) return _cursor.callproc(self, procname, vars)
def _build_index(self): def _build_index(self):
if self.__query_executed == 1 and self.description: if self._query_executed == 1 and self.description:
for i in range(len(self.description)): for i in range(len(self.description)):
self.index[self.description[i][0]] = i self.index[self.description[i][0]] = i
self.__query_executed = 0 self._query_executed = 0
def fetchone(self):
res = _cursor.fetchone(self)
if self.__query_executed:
self._build_index()
return res
def fetchmany(self, size=None):
res = _cursor.fetchmany(self, size)
if self.__query_executed:
self._build_index()
return res
def fetchall(self):
res = _cursor.fetchall(self)
if self.__query_executed:
self._build_index()
return res
def next(self):
res = _cursor.fetchone(self)
if res is None:
raise StopIteration()
if self.__query_executed:
self._build_index()
return res
class DictRow(list): class DictRow(list):
"""A row object that allow by-colun-name access to data.""" """A row object that allow by-colun-name access to data."""
@ -121,7 +132,52 @@ class DictRow(list):
for n, v in self._index.items(): for n, v in self._index.items():
yield n, list.__getitem__(self, v) yield n, list.__getitem__(self, v)
class RealDictConnection(_connection):
"""A connection that uses RealDictCursor automatically."""
def cursor(self):
return _connection.cursor(self, cursor_factory=RealDictCursor)
class RealDictCursor(DictCursorBase):
"""A cursor that uses a real dict as the base type for rows.
Note that this cursor is extremely specialized and does not allow
the normal access (using integer indices) to fetched data. If you need
to access database rows both as a dictionary and a list, then use
the generic DictCursor instead of RealDictCursor.
"""
def __init__(self, *args, **kwargs):
kwargs['row_factory'] = RealDictRow
DictCursorBase.__init__(self, *args, **kwargs)
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 _build_index(self):
if self._query_executed == 1 and self.description:
for item in self.description:
self.column_mapping.append(item[0])
self._query_executed = 0
class RealDictRow(dict):
def __init__(self, cursor):
dict.__init__(self)
self._column_mapping = cursor.column_mapping
def __setitem__(self, name, value):
if type(name) == type(0):
name = self._column_mapping[name]
return dict.__setitem__(self, name, value)
class LoggingConnection(_connection): class LoggingConnection(_connection):
"""A connection that logs all queries to a file or logger object.""" """A connection that logs all queries to a file or logger object."""