diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-02-06 01:31:50 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-02-06 01:31:50 +0000 |
| commit | 1c16c47a47a6b66f5267eb915192b9e2f8674972 (patch) | |
| tree | 8367048ea5fc2b555e8d7cd63cc1309ffb3924b9 /lib/sqlalchemy/engine/base.py | |
| parent | 55d455d7d0d4d67d9f3d13cf74de471c15ce5d50 (diff) | |
| download | sqlalchemy-1c16c47a47a6b66f5267eb915192b9e2f8674972.tar.gz | |
- added PrefetchingResultProxy support to pre-fetch LOB columns when they are
known to be present, fixes [ticket:435]
Diffstat (limited to 'lib/sqlalchemy/engine/base.py')
| -rw-r--r-- | lib/sqlalchemy/engine/base.py | 62 |
1 files changed, 56 insertions, 6 deletions
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 8871cac1d..3ec3e028f 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -121,6 +121,9 @@ class Dialect(sql.AbstractDialect): def create_cursor(self, connection): """return a new cursor generated from the given connection""" raise NotImplementedError() + def create_result_proxy_args(self, connection, cursor): + """returns a dictionary of arguments that should be passed to ResultProxy().""" + raise NotImplementedError() def compile(self, clauseelement, parameters=None): """compile the given ClauseElement using this Dialect. @@ -265,7 +268,8 @@ class Connection(Connectable): return default.accept_schema_visitor(self.__engine.dialect.defaultrunner(self.__engine, self.proxy, **kwargs)) def execute_text(self, statement, parameters=None): cursor = self._execute_raw(statement, parameters) - return ResultProxy(self.__engine, self, cursor) + rpargs = self.__engine.dialect.create_result_proxy_args(self, cursor) + return ResultProxy(self.__engine, self, cursor, **rpargs) def _params_to_listofdicts(self, *multiparams, **params): if len(multiparams) == 0: return [params] @@ -304,7 +308,8 @@ class Connection(Connectable): context.pre_exec(self.__engine, proxy, compiled, parameters) proxy(str(compiled), parameters) context.post_exec(self.__engine, proxy, compiled, parameters) - return ResultProxy(self.__engine, self, cursor, context, typemap=compiled.typemap, columns=compiled.columns) + rpargs = self.__engine.dialect.create_result_proxy_args(self, cursor) + return ResultProxy(self.__engine, self, cursor, context, typemap=compiled.typemap, columns=compiled.columns, **rpargs) # poor man's multimethod/generic function thingy executors = { @@ -551,8 +556,14 @@ class ResultProxy(object): return self def convert_result_value(self, arg, engine): raise exceptions.InvalidRequestError("Ambiguous column name '%s' in result set! try 'use_labels' option on select statement." % (self.key)) + + def __new__(cls, *args, **kwargs): + if cls is ResultProxy and kwargs.has_key('should_prefetch') and kwargs['should_prefetch']: + return PrefetchingResultProxy(*args, **kwargs) + else: + return object.__new__(cls, *args, **kwargs) - def __init__(self, engine, connection, cursor, executioncontext=None, typemap=None, columns=None): + def __init__(self, engine, connection, cursor, executioncontext=None, typemap=None, columns=None, should_prefetch=None): """ResultProxy objects are constructed via the execute() method on SQLEngine.""" self.connection = connection self.dialect = engine.dialect @@ -612,6 +623,7 @@ class ResultProxy(object): try: return self.__key_cache[key] except KeyError: + # TODO: use has_key on these, too many potential KeyErrors being raised if isinstance(key, sql.ColumnElement): try: rec = self.props[key._label.lower()] @@ -714,9 +726,6 @@ class ResultProxy(object): if row is not None: return RowProxy(self, row) else: - # controversy! can we auto-close the cursor after results are consumed ? - # what if the returned rows are still hanging around, and are "live" objects - # and not just plain tuples ? self.close() return None @@ -730,7 +739,48 @@ class ResultProxy(object): return None finally: self.close() + +class PrefetchingResultProxy(ResultProxy): + """ResultProxy that loads all columns into memory each time fetchone() is + called. If fetchmany() or fetchall() are called, the full grid of results + is fetched. + """ + def _get_col(self, row, key): + rec = self._convert_key(key) + return row[rec[1]] + def fetchall(self): + l = [] + while True: + row = self.fetchone() + if row is not None: + l.append(row) + else: + break + return l + + def fetchmany(self, size=None): + if size is None: + return self.fetchall() + l = [] + for i in xrange(size): + row = self.fetchone() + if row is not None: + l.append(row) + else: + break + return l + + def fetchone(self): + sup = super(PrefetchingResultProxy, self) + row = self.cursor.fetchone() + if row is not None: + row = [sup._get_col(row, i) for i in xrange(len(row))] + return RowProxy(self, row) + else: + self.close() + return None + class RowProxy(object): """proxies a single cursor row for a parent ResultProxy. Mostly follows "ordered dictionary" behavior, mapping result values to the string-based column name, |
