diff options
Diffstat (limited to 'lib/sqlalchemy/future/result.py')
| -rw-r--r-- | lib/sqlalchemy/future/result.py | 171 | 
1 files changed, 171 insertions, 0 deletions
diff --git a/lib/sqlalchemy/future/result.py b/lib/sqlalchemy/future/result.py new file mode 100644 index 000000000..583ff957a --- /dev/null +++ b/lib/sqlalchemy/future/result.py @@ -0,0 +1,171 @@ +import operator + +from .. import util +from ..engine.result import _baserow_usecext +from ..engine.result import BaseResult +from ..engine.result import CursorResultMetaData +from ..engine.result import DefaultCursorFetchStrategy +from ..engine.result import Row +from ..sql import util as sql_util +from ..sql.base import _generative +from ..sql.base import Generative + + +class Result(Generative, BaseResult): +    """Interim "future" result proxy so that dialects can build on +    upcoming 2.0 patterns. + + +    """ + +    _process_row = Row +    _cursor_metadata = CursorResultMetaData +    _cursor_strategy_cls = DefaultCursorFetchStrategy + +    _column_slice_filter = None +    _post_creational_filter = None + +    def close(self): +        """Close this :class:`.Result`. + +        This closes out the underlying DBAPI cursor corresponding +        to the statement execution, if one is still present.  Note that the +        DBAPI cursor is automatically released when the :class:`.Result` +        exhausts all available rows.  :meth:`.Result.close` is generally +        an optional method except in the case when discarding a +        :class:`.Result` that still has additional rows pending for fetch. + +        After this method is called, it is no longer valid to call upon +        the fetch methods, which will raise a :class:`.ResourceClosedError` +        on subsequent use. + +        .. seealso:: + +            :ref:`connections_toplevel` + +        """ +        self._soft_close(hard=True) + +    def columns(self, *col_expressions): +        indexes = [] +        for key in col_expressions: +            try: +                rec = self._keymap[key] +            except KeyError: +                rec = self._key_fallback(key, True) +                if rec is None: +                    return None + +            index, obj = rec[0:2] + +            if index is None: +                self._metadata._raise_for_ambiguous_column_name(obj) +            indexes.append(index) +        return self._column_slices(indexes) + +    def scalars(self): +        result = self._column_slices(0) +        result._post_creational_filter = operator.itemgetter(0) +        return result + +    @_generative +    def _column_slices(self, indexes): +        if _baserow_usecext: +            self._column_slice_filter = self._metadata._tuplegetter(*indexes) +        else: +            self._column_slice_filter = self._metadata._pure_py_tuplegetter( +                *indexes +            ) + +    @_generative +    def mappings(self): +        self._post_creational_filter = operator.attrgetter("_mapping") + +    def _row_getter(self): +        process_row = self._process_row +        metadata = self._metadata +        keymap = metadata._keymap +        processors = metadata._processors + +        fns = () + +        if self._echo: +            log = self.context.engine.logger.debug + +            def log_row(row): +                log("Row %r", sql_util._repr_row(row)) +                return row + +            fns += (log_row,) + +        if self._column_slice_filter: +            fns += (self._column_slice_filter,) + +        if self._post_creational_filter: +            fns += (self._post_creational_filter,) + +        def make_row(row): +            row = process_row(metadata, processors, keymap, row) +            for fn in fns: +                row = fn(row) +            return row + +        return make_row + +    def _safe_fetchone_impl(self): +        try: +            return self.cursor_strategy.fetchone() +        except BaseException as e: +            self.connection._handle_dbapi_exception( +                e, None, None, self.cursor, self.context +            ) + +    def _safe_fetchall_impl(self): +        try: +            result = self.cursor_strategy.fetchall() +            self._soft_close() +            return result +        except BaseException as e: +            self.connection._handle_dbapi_exception( +                e, None, None, self.cursor, self.context +            ) + +    def _safe_fetchmany_impl(self, size=None): +        try: +            l = self.process_rows(self.cursor_strategy.fetchmany(size)) +            if len(l) == 0: +                self._soft_close() +            return l +        except BaseException as e: +            self.connection._handle_dbapi_exception( +                e, None, None, self.cursor, self.context +            ) + +    def __iter__(self): +        getter = self._row_getter() +        return (getter(r) for r in self._safe_fetchall_impl()) + +    def _onerow(self): +        getter = self._row_getter() +        row = self._safe_fetchone_impl() +        if row is None: +            return None +        else: +            return getter(row) + +    def all(self): +        getter = self._row_getter() +        return [getter(r) for r in self._safe_fetchall_impl()] + +    def first(self): +        getter = self._row_getter() +        row = self._safe_fetchone_impl() +        if row is None: +            return None +        else: +            row = getter(row) +            second_row = self._safe_fetchone_impl() +            if second_row is not None: +                self._soft_close() +                util.warn("Additional rows remain") +            return row  | 
