mirror of
https://github.com/djohnlewis/stackdump
synced 2025-01-23 07:01:41 +00:00
92 lines
3.9 KiB
Python
92 lines
3.9 KiB
Python
|
from sqlobject import sqlbuilder
|
||
|
from sqlobject.classregistry import findClass
|
||
|
from sqlobject.dbconnection import Iteration
|
||
|
|
||
|
class InheritableIteration(Iteration):
|
||
|
# Default array size for cursor.fetchmany()
|
||
|
defaultArraySize = 10000
|
||
|
|
||
|
def __init__(self, dbconn, rawconn, select, keepConnection=False):
|
||
|
super(InheritableIteration, self).__init__(dbconn, rawconn, select, keepConnection)
|
||
|
self.lazyColumns = select.ops.get('lazyColumns', False)
|
||
|
self.cursor.arraysize = self.defaultArraySize
|
||
|
self._results = []
|
||
|
# Find the index of the childName column
|
||
|
childNameIdx = None
|
||
|
columns = select.sourceClass.sqlmeta.columnList
|
||
|
for i, column in enumerate(columns):
|
||
|
if column.name == "childName":
|
||
|
childNameIdx = i
|
||
|
break
|
||
|
self._childNameIdx = childNameIdx
|
||
|
|
||
|
def next(self):
|
||
|
if not self._results:
|
||
|
self._results = list(self.cursor.fetchmany())
|
||
|
if not self.lazyColumns: self.fetchChildren()
|
||
|
if not self._results:
|
||
|
self._cleanup()
|
||
|
raise StopIteration
|
||
|
result = self._results[0]
|
||
|
del self._results[0]
|
||
|
if self.lazyColumns:
|
||
|
obj = self.select.sourceClass.get(result[0], connection=self.dbconn)
|
||
|
return obj
|
||
|
else:
|
||
|
id = result[0]
|
||
|
if id in self._childrenResults:
|
||
|
childResults = self._childrenResults[id]
|
||
|
del self._childrenResults[id]
|
||
|
else:
|
||
|
childResults = None
|
||
|
obj = self.select.sourceClass.get(id, selectResults=result[1:],
|
||
|
childResults=childResults, connection=self.dbconn)
|
||
|
return obj
|
||
|
|
||
|
def fetchChildren(self):
|
||
|
"""Prefetch childrens' data
|
||
|
|
||
|
Fetch childrens' data for every subclass in one big .select()
|
||
|
to avoid .get() fetching it one by one.
|
||
|
"""
|
||
|
self._childrenResults = {}
|
||
|
if self._childNameIdx is None:
|
||
|
return
|
||
|
childIdsNames = {}
|
||
|
childNameIdx = self._childNameIdx
|
||
|
for result in self._results:
|
||
|
childName = result[childNameIdx+1]
|
||
|
if childName:
|
||
|
ids = childIdsNames.get(childName)
|
||
|
if ids is None:
|
||
|
ids = childIdsNames[childName] = []
|
||
|
ids.append(result[0])
|
||
|
dbconn = self.dbconn
|
||
|
rawconn = self.rawconn
|
||
|
cursor = rawconn.cursor()
|
||
|
registry = self.select.sourceClass.sqlmeta.registry
|
||
|
for childName, ids in childIdsNames.items():
|
||
|
klass = findClass(childName, registry)
|
||
|
if len(ids) == 1:
|
||
|
select = klass.select(klass.q.id == ids[0],
|
||
|
childUpdate=True, connection=dbconn)
|
||
|
else:
|
||
|
select = klass.select(sqlbuilder.IN(klass.q.id, ids),
|
||
|
childUpdate=True, connection=dbconn)
|
||
|
query = dbconn.queryForSelect(select)
|
||
|
if dbconn.debug:
|
||
|
dbconn.printDebug(rawconn, query, 'Select children of the class %s' % childName)
|
||
|
self.dbconn._executeRetry(rawconn, cursor, query)
|
||
|
for result in cursor.fetchall():
|
||
|
# Inheritance child classes may have no own columns
|
||
|
# (that makes sense when child class has a join
|
||
|
# that does not apply to parent class objects).
|
||
|
# In such cases result[1:] gives an empty tuple
|
||
|
# which is interpreted as "no results fetched" in .get().
|
||
|
# So .get() issues another query which is absolutely
|
||
|
# meaningless (like "SELECT NULL FROM child WHERE id=1").
|
||
|
# In order to avoid this, we replace empty results
|
||
|
# with non-empty tuple. Extra values in selectResults
|
||
|
# are Ok - they will be ignored by ._SO_selectInit().
|
||
|
self._childrenResults[result[0]] = result[1:] or (None,)
|