You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1724 lines
52KB

  1. # engine/result.py
  2. # Copyright (C) 2005-2021 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. """Define generic result set constructs."""
  8. import functools
  9. import itertools
  10. import operator
  11. from .row import _baserow_usecext
  12. from .row import Row
  13. from .. import exc
  14. from .. import util
  15. from ..sql.base import _generative
  16. from ..sql.base import HasMemoized
  17. from ..sql.base import InPlaceGenerative
  18. from ..util import collections_abc
  19. from ..util import py2k
  20. if _baserow_usecext:
  21. from sqlalchemy.cresultproxy import tuplegetter
  22. _row_as_tuple = tuplegetter
  23. else:
  24. def tuplegetter(*indexes):
  25. it = operator.itemgetter(*indexes)
  26. if len(indexes) > 1:
  27. return it
  28. else:
  29. return lambda row: (it(row),)
  30. def _row_as_tuple(*indexes):
  31. # circumvent LegacyRow.__getitem__ pointing to
  32. # _get_by_key_impl_mapping for now. otherwise we could
  33. # use itemgetter
  34. getters = [
  35. operator.methodcaller("_get_by_int_impl", index)
  36. for index in indexes
  37. ]
  38. return lambda rec: tuple([getter(rec) for getter in getters])
  39. class ResultMetaData(object):
  40. """Base for metadata about result rows."""
  41. __slots__ = ()
  42. _tuplefilter = None
  43. _translated_indexes = None
  44. _unique_filters = None
  45. @property
  46. def keys(self):
  47. return RMKeyView(self)
  48. def _has_key(self, key):
  49. raise NotImplementedError()
  50. def _for_freeze(self):
  51. raise NotImplementedError()
  52. def _key_fallback(self, key, err, raiseerr=True):
  53. assert raiseerr
  54. util.raise_(KeyError(key), replace_context=err)
  55. def _warn_for_nonint(self, key):
  56. util.warn_deprecated_20(
  57. "Retrieving row members using strings or other non-integers is "
  58. "deprecated; use row._mapping for a dictionary interface "
  59. "to the row"
  60. )
  61. def _raise_for_nonint(self, key):
  62. raise TypeError(
  63. "TypeError: tuple indices must be integers or slices, not %s"
  64. % type(key).__name__
  65. )
  66. def _index_for_key(self, keys, raiseerr):
  67. raise NotImplementedError()
  68. def _metadata_for_keys(self, key):
  69. raise NotImplementedError()
  70. def _reduce(self, keys):
  71. raise NotImplementedError()
  72. def _getter(self, key, raiseerr=True):
  73. index = self._index_for_key(key, raiseerr)
  74. if index is not None:
  75. return operator.itemgetter(index)
  76. else:
  77. return None
  78. def _row_as_tuple_getter(self, keys):
  79. indexes = self._indexes_for_keys(keys)
  80. return _row_as_tuple(*indexes)
  81. class RMKeyView(collections_abc.KeysView):
  82. __slots__ = ("_parent", "_keys")
  83. def __init__(self, parent):
  84. self._parent = parent
  85. self._keys = [k for k in parent._keys if k is not None]
  86. def __len__(self):
  87. return len(self._keys)
  88. def __repr__(self):
  89. return "{0.__class__.__name__}({0._keys!r})".format(self)
  90. def __iter__(self):
  91. return iter(self._keys)
  92. def __contains__(self, item):
  93. if not _baserow_usecext and isinstance(item, int):
  94. return False
  95. # note this also includes special key fallback behaviors
  96. # which also don't seem to be tested in test_resultset right now
  97. return self._parent._has_key(item)
  98. def __eq__(self, other):
  99. return list(other) == list(self)
  100. def __ne__(self, other):
  101. return list(other) != list(self)
  102. class SimpleResultMetaData(ResultMetaData):
  103. """result metadata for in-memory collections."""
  104. __slots__ = (
  105. "_keys",
  106. "_keymap",
  107. "_processors",
  108. "_tuplefilter",
  109. "_translated_indexes",
  110. "_unique_filters",
  111. )
  112. def __init__(
  113. self,
  114. keys,
  115. extra=None,
  116. _processors=None,
  117. _tuplefilter=None,
  118. _translated_indexes=None,
  119. _unique_filters=None,
  120. ):
  121. self._keys = list(keys)
  122. self._tuplefilter = _tuplefilter
  123. self._translated_indexes = _translated_indexes
  124. self._unique_filters = _unique_filters
  125. if extra:
  126. recs_names = [
  127. (
  128. (name,) + extras,
  129. (index, name, extras),
  130. )
  131. for index, (name, extras) in enumerate(zip(self._keys, extra))
  132. ]
  133. else:
  134. recs_names = [
  135. ((name,), (index, name, ()))
  136. for index, name in enumerate(self._keys)
  137. ]
  138. self._keymap = {key: rec for keys, rec in recs_names for key in keys}
  139. self._processors = _processors
  140. def _has_key(self, key):
  141. return key in self._keymap
  142. def _for_freeze(self):
  143. unique_filters = self._unique_filters
  144. if unique_filters and self._tuplefilter:
  145. unique_filters = self._tuplefilter(unique_filters)
  146. # TODO: are we freezing the result with or without uniqueness
  147. # applied?
  148. return SimpleResultMetaData(
  149. self._keys,
  150. extra=[self._keymap[key][2] for key in self._keys],
  151. _unique_filters=unique_filters,
  152. )
  153. def __getstate__(self):
  154. return {
  155. "_keys": self._keys,
  156. "_translated_indexes": self._translated_indexes,
  157. }
  158. def __setstate__(self, state):
  159. if state["_translated_indexes"]:
  160. _translated_indexes = state["_translated_indexes"]
  161. _tuplefilter = tuplegetter(*_translated_indexes)
  162. else:
  163. _translated_indexes = _tuplefilter = None
  164. self.__init__(
  165. state["_keys"],
  166. _translated_indexes=_translated_indexes,
  167. _tuplefilter=_tuplefilter,
  168. )
  169. def _contains(self, value, row):
  170. return value in row._data
  171. def _index_for_key(self, key, raiseerr=True):
  172. if int in key.__class__.__mro__:
  173. key = self._keys[key]
  174. try:
  175. rec = self._keymap[key]
  176. except KeyError as ke:
  177. rec = self._key_fallback(key, ke, raiseerr)
  178. return rec[0]
  179. def _indexes_for_keys(self, keys):
  180. return [self._keymap[key][0] for key in keys]
  181. def _metadata_for_keys(self, keys):
  182. for key in keys:
  183. if int in key.__class__.__mro__:
  184. key = self._keys[key]
  185. try:
  186. rec = self._keymap[key]
  187. except KeyError as ke:
  188. rec = self._key_fallback(key, ke, True)
  189. yield rec
  190. def _reduce(self, keys):
  191. try:
  192. metadata_for_keys = [
  193. self._keymap[
  194. self._keys[key] if int in key.__class__.__mro__ else key
  195. ]
  196. for key in keys
  197. ]
  198. except KeyError as ke:
  199. self._key_fallback(ke.args[0], ke, True)
  200. indexes, new_keys, extra = zip(*metadata_for_keys)
  201. if self._translated_indexes:
  202. indexes = [self._translated_indexes[idx] for idx in indexes]
  203. tup = tuplegetter(*indexes)
  204. new_metadata = SimpleResultMetaData(
  205. new_keys,
  206. extra=extra,
  207. _tuplefilter=tup,
  208. _translated_indexes=indexes,
  209. _processors=self._processors,
  210. _unique_filters=self._unique_filters,
  211. )
  212. return new_metadata
  213. def result_tuple(fields, extra=None):
  214. parent = SimpleResultMetaData(fields, extra)
  215. return functools.partial(
  216. Row, parent, parent._processors, parent._keymap, Row._default_key_style
  217. )
  218. # a symbol that indicates to internal Result methods that
  219. # "no row is returned". We can't use None for those cases where a scalar
  220. # filter is applied to rows.
  221. _NO_ROW = util.symbol("NO_ROW")
  222. class ResultInternal(InPlaceGenerative):
  223. _real_result = None
  224. _generate_rows = True
  225. _unique_filter_state = None
  226. _post_creational_filter = None
  227. @HasMemoized.memoized_attribute
  228. def _row_getter(self):
  229. real_result = self._real_result if self._real_result else self
  230. if real_result._source_supports_scalars:
  231. if not self._generate_rows:
  232. return None
  233. else:
  234. _proc = real_result._process_row
  235. def process_row(
  236. metadata, processors, keymap, key_style, scalar_obj
  237. ):
  238. return _proc(
  239. metadata, processors, keymap, key_style, (scalar_obj,)
  240. )
  241. else:
  242. process_row = real_result._process_row
  243. key_style = real_result._process_row._default_key_style
  244. metadata = self._metadata
  245. keymap = metadata._keymap
  246. processors = metadata._processors
  247. tf = metadata._tuplefilter
  248. if tf and not real_result._source_supports_scalars:
  249. if processors:
  250. processors = tf(processors)
  251. _make_row_orig = functools.partial(
  252. process_row, metadata, processors, keymap, key_style
  253. )
  254. def make_row(row):
  255. return _make_row_orig(tf(row))
  256. else:
  257. make_row = functools.partial(
  258. process_row, metadata, processors, keymap, key_style
  259. )
  260. fns = ()
  261. if real_result._row_logging_fn:
  262. fns = (real_result._row_logging_fn,)
  263. else:
  264. fns = ()
  265. if fns:
  266. _make_row = make_row
  267. def make_row(row):
  268. row = _make_row(row)
  269. for fn in fns:
  270. row = fn(row)
  271. return row
  272. return make_row
  273. @HasMemoized.memoized_attribute
  274. def _iterator_getter(self):
  275. make_row = self._row_getter
  276. post_creational_filter = self._post_creational_filter
  277. if self._unique_filter_state:
  278. uniques, strategy = self._unique_strategy
  279. def iterrows(self):
  280. for row in self._fetchiter_impl():
  281. obj = make_row(row) if make_row else row
  282. hashed = strategy(obj) if strategy else obj
  283. if hashed in uniques:
  284. continue
  285. uniques.add(hashed)
  286. if post_creational_filter:
  287. obj = post_creational_filter(obj)
  288. yield obj
  289. else:
  290. def iterrows(self):
  291. for row in self._fetchiter_impl():
  292. row = make_row(row) if make_row else row
  293. if post_creational_filter:
  294. row = post_creational_filter(row)
  295. yield row
  296. return iterrows
  297. def _raw_all_rows(self):
  298. make_row = self._row_getter
  299. rows = self._fetchall_impl()
  300. return [make_row(row) for row in rows]
  301. def _allrows(self):
  302. post_creational_filter = self._post_creational_filter
  303. make_row = self._row_getter
  304. rows = self._fetchall_impl()
  305. if make_row:
  306. made_rows = [make_row(row) for row in rows]
  307. else:
  308. made_rows = rows
  309. if self._unique_filter_state:
  310. uniques, strategy = self._unique_strategy
  311. rows = [
  312. made_row
  313. for made_row, sig_row in [
  314. (
  315. made_row,
  316. strategy(made_row) if strategy else made_row,
  317. )
  318. for made_row in made_rows
  319. ]
  320. if sig_row not in uniques and not uniques.add(sig_row)
  321. ]
  322. else:
  323. rows = made_rows
  324. if post_creational_filter:
  325. rows = [post_creational_filter(row) for row in rows]
  326. return rows
  327. @HasMemoized.memoized_attribute
  328. def _onerow_getter(self):
  329. make_row = self._row_getter
  330. post_creational_filter = self._post_creational_filter
  331. if self._unique_filter_state:
  332. uniques, strategy = self._unique_strategy
  333. def onerow(self):
  334. _onerow = self._fetchone_impl
  335. while True:
  336. row = _onerow()
  337. if row is None:
  338. return _NO_ROW
  339. else:
  340. obj = make_row(row) if make_row else row
  341. hashed = strategy(obj) if strategy else obj
  342. if hashed in uniques:
  343. continue
  344. else:
  345. uniques.add(hashed)
  346. if post_creational_filter:
  347. obj = post_creational_filter(obj)
  348. return obj
  349. else:
  350. def onerow(self):
  351. row = self._fetchone_impl()
  352. if row is None:
  353. return _NO_ROW
  354. else:
  355. row = make_row(row) if make_row else row
  356. if post_creational_filter:
  357. row = post_creational_filter(row)
  358. return row
  359. return onerow
  360. @HasMemoized.memoized_attribute
  361. def _manyrow_getter(self):
  362. make_row = self._row_getter
  363. post_creational_filter = self._post_creational_filter
  364. if self._unique_filter_state:
  365. uniques, strategy = self._unique_strategy
  366. def filterrows(make_row, rows, strategy, uniques):
  367. if make_row:
  368. rows = [make_row(row) for row in rows]
  369. if strategy:
  370. made_rows = (
  371. (made_row, strategy(made_row)) for made_row in rows
  372. )
  373. else:
  374. made_rows = ((made_row, made_row) for made_row in rows)
  375. return [
  376. made_row
  377. for made_row, sig_row in made_rows
  378. if sig_row not in uniques and not uniques.add(sig_row)
  379. ]
  380. def manyrows(self, num):
  381. collect = []
  382. _manyrows = self._fetchmany_impl
  383. if num is None:
  384. # if None is passed, we don't know the default
  385. # manyrows number, DBAPI has this as cursor.arraysize
  386. # different DBAPIs / fetch strategies may be different.
  387. # do a fetch to find what the number is. if there are
  388. # only fewer rows left, then it doesn't matter.
  389. real_result = (
  390. self._real_result if self._real_result else self
  391. )
  392. if real_result._yield_per:
  393. num_required = num = real_result._yield_per
  394. else:
  395. rows = _manyrows(num)
  396. num = len(rows)
  397. collect.extend(
  398. filterrows(make_row, rows, strategy, uniques)
  399. )
  400. num_required = num - len(collect)
  401. else:
  402. num_required = num
  403. while num_required:
  404. rows = _manyrows(num_required)
  405. if not rows:
  406. break
  407. collect.extend(
  408. filterrows(make_row, rows, strategy, uniques)
  409. )
  410. num_required = num - len(collect)
  411. if post_creational_filter:
  412. collect = [post_creational_filter(row) for row in collect]
  413. return collect
  414. else:
  415. def manyrows(self, num):
  416. if num is None:
  417. real_result = (
  418. self._real_result if self._real_result else self
  419. )
  420. num = real_result._yield_per
  421. rows = self._fetchmany_impl(num)
  422. if make_row:
  423. rows = [make_row(row) for row in rows]
  424. if post_creational_filter:
  425. rows = [post_creational_filter(row) for row in rows]
  426. return rows
  427. return manyrows
  428. def _only_one_row(
  429. self,
  430. raise_for_second_row,
  431. raise_for_none,
  432. scalar,
  433. ):
  434. onerow = self._fetchone_impl
  435. row = onerow(hard_close=True)
  436. if row is None:
  437. if raise_for_none:
  438. raise exc.NoResultFound(
  439. "No row was found when one was required"
  440. )
  441. else:
  442. return None
  443. if scalar and self._source_supports_scalars:
  444. self._generate_rows = False
  445. make_row = None
  446. else:
  447. make_row = self._row_getter
  448. try:
  449. row = make_row(row) if make_row else row
  450. except:
  451. self._soft_close(hard=True)
  452. raise
  453. if raise_for_second_row:
  454. if self._unique_filter_state:
  455. # for no second row but uniqueness, need to essentially
  456. # consume the entire result :(
  457. uniques, strategy = self._unique_strategy
  458. existing_row_hash = strategy(row) if strategy else row
  459. while True:
  460. next_row = onerow(hard_close=True)
  461. if next_row is None:
  462. next_row = _NO_ROW
  463. break
  464. try:
  465. next_row = make_row(next_row) if make_row else next_row
  466. if strategy:
  467. if existing_row_hash == strategy(next_row):
  468. continue
  469. elif row == next_row:
  470. continue
  471. # here, we have a row and it's different
  472. break
  473. except:
  474. self._soft_close(hard=True)
  475. raise
  476. else:
  477. next_row = onerow(hard_close=True)
  478. if next_row is None:
  479. next_row = _NO_ROW
  480. if next_row is not _NO_ROW:
  481. self._soft_close(hard=True)
  482. raise exc.MultipleResultsFound(
  483. "Multiple rows were found when exactly one was required"
  484. if raise_for_none
  485. else "Multiple rows were found when one or none "
  486. "was required"
  487. )
  488. else:
  489. next_row = _NO_ROW
  490. # if we checked for second row then that would have
  491. # closed us :)
  492. self._soft_close(hard=True)
  493. if not scalar:
  494. post_creational_filter = self._post_creational_filter
  495. if post_creational_filter:
  496. row = post_creational_filter(row)
  497. if scalar and make_row:
  498. return row[0]
  499. else:
  500. return row
  501. def _iter_impl(self):
  502. return self._iterator_getter(self)
  503. def _next_impl(self):
  504. row = self._onerow_getter(self)
  505. if row is _NO_ROW:
  506. raise StopIteration()
  507. else:
  508. return row
  509. @_generative
  510. def _column_slices(self, indexes):
  511. real_result = self._real_result if self._real_result else self
  512. if real_result._source_supports_scalars and len(indexes) == 1:
  513. self._generate_rows = False
  514. else:
  515. self._generate_rows = True
  516. self._metadata = self._metadata._reduce(indexes)
  517. @HasMemoized.memoized_attribute
  518. def _unique_strategy(self):
  519. uniques, strategy = self._unique_filter_state
  520. real_result = (
  521. self._real_result if self._real_result is not None else self
  522. )
  523. if not strategy and self._metadata._unique_filters:
  524. if (
  525. real_result._source_supports_scalars
  526. and not self._generate_rows
  527. ):
  528. strategy = self._metadata._unique_filters[0]
  529. else:
  530. filters = self._metadata._unique_filters
  531. if self._metadata._tuplefilter:
  532. filters = self._metadata._tuplefilter(filters)
  533. strategy = operator.methodcaller("_filter_on_values", filters)
  534. return uniques, strategy
  535. class _WithKeys(object):
  536. # used mainly to share documentation on the keys method.
  537. # py2k does not allow overriding the __doc__ attribute.
  538. def keys(self):
  539. """Return an iterable view which yields the string keys that would
  540. be represented by each :class:`.Row`.
  541. The keys can represent the labels of the columns returned by a core
  542. statement or the names of the orm classes returned by an orm
  543. execution.
  544. The view also can be tested for key containment using the Python
  545. ``in`` operator, which will test both for the string keys represented
  546. in the view, as well as for alternate keys such as column objects.
  547. .. versionchanged:: 1.4 a key view object is returned rather than a
  548. plain list.
  549. """
  550. return self._metadata.keys
  551. class Result(_WithKeys, ResultInternal):
  552. """Represent a set of database results.
  553. .. versionadded:: 1.4 The :class:`.Result` object provides a completely
  554. updated usage model and calling facade for SQLAlchemy Core and
  555. SQLAlchemy ORM. In Core, it forms the basis of the
  556. :class:`.CursorResult` object which replaces the previous
  557. :class:`.ResultProxy` interface. When using the ORM, a higher level
  558. object called :class:`.ChunkedIteratorResult` is normally used.
  559. .. seealso::
  560. :ref:`tutorial_fetching_rows` - in the :doc:`/tutorial/index`
  561. """
  562. _process_row = Row
  563. _row_logging_fn = None
  564. _source_supports_scalars = False
  565. _yield_per = None
  566. _attributes = util.immutabledict()
  567. def __init__(self, cursor_metadata):
  568. self._metadata = cursor_metadata
  569. def _soft_close(self, hard=False):
  570. raise NotImplementedError()
  571. @_generative
  572. def yield_per(self, num):
  573. """Configure the row-fetching strategy to fetch num rows at a time.
  574. This impacts the underlying behavior of the result when iterating over
  575. the result object, or otherwise making use of methods such as
  576. :meth:`_engine.Result.fetchone` that return one row at a time. Data
  577. from the underlying cursor or other data source will be buffered up to
  578. this many rows in memory, and the buffered collection will then be
  579. yielded out one row at at time or as many rows are requested. Each time
  580. the buffer clears, it will be refreshed to this many rows or as many
  581. rows remain if fewer remain.
  582. The :meth:`_engine.Result.yield_per` method is generally used in
  583. conjunction with the
  584. :paramref:`_engine.Connection.execution_options.stream_results`
  585. execution option, which will allow the database dialect in use to make
  586. use of a server side cursor, if the DBAPI supports it.
  587. Most DBAPIs do not use server side cursors by default, which means all
  588. rows will be fetched upfront from the database regardless of the
  589. :meth:`_engine.Result.yield_per` setting. However,
  590. :meth:`_engine.Result.yield_per` may still be useful in that it batches
  591. the SQLAlchemy-side processing of the raw data from the database, and
  592. additionally when used for ORM scenarios will batch the conversion of
  593. database rows into ORM entity rows.
  594. .. versionadded:: 1.4
  595. :param num: number of rows to fetch each time the buffer is refilled.
  596. If set to a value below 1, fetches all rows for the next buffer.
  597. """
  598. self._yield_per = num
  599. @_generative
  600. def unique(self, strategy=None):
  601. """Apply unique filtering to the objects returned by this
  602. :class:`_engine.Result`.
  603. When this filter is applied with no arguments, the rows or objects
  604. returned will filtered such that each row is returned uniquely. The
  605. algorithm used to determine this uniqueness is by default the Python
  606. hashing identity of the whole tuple. In some cases a specialized
  607. per-entity hashing scheme may be used, such as when using the ORM, a
  608. scheme is applied which works against the primary key identity of
  609. returned objects.
  610. The unique filter is applied **after all other filters**, which means
  611. if the columns returned have been refined using a method such as the
  612. :meth:`_engine.Result.columns` or :meth:`_engine.Result.scalars`
  613. method, the uniquing is applied to **only the column or columns
  614. returned**. This occurs regardless of the order in which these
  615. methods have been called upon the :class:`_engine.Result` object.
  616. The unique filter also changes the calculus used for methods like
  617. :meth:`_engine.Result.fetchmany` and :meth:`_engine.Result.partitions`.
  618. When using :meth:`_engine.Result.unique`, these methods will continue
  619. to yield the number of rows or objects requested, after uniquing
  620. has been applied. However, this necessarily impacts the buffering
  621. behavior of the underlying cursor or datasource, such that multiple
  622. underlying calls to ``cursor.fetchmany()`` may be necessary in order
  623. to accumulate enough objects in order to provide a unique collection
  624. of the requested size.
  625. :param strategy: a callable that will be applied to rows or objects
  626. being iterated, which should return an object that represents the
  627. unique value of the row. A Python ``set()`` is used to store
  628. these identities. If not passed, a default uniqueness strategy
  629. is used which may have been assembled by the source of this
  630. :class:`_engine.Result` object.
  631. """
  632. self._unique_filter_state = (set(), strategy)
  633. def columns(self, *col_expressions):
  634. r"""Establish the columns that should be returned in each row.
  635. This method may be used to limit the columns returned as well
  636. as to reorder them. The given list of expressions are normally
  637. a series of integers or string key names. They may also be
  638. appropriate :class:`.ColumnElement` objects which correspond to
  639. a given statement construct.
  640. E.g.::
  641. statement = select(table.c.x, table.c.y, table.c.z)
  642. result = connection.execute(statement)
  643. for z, y in result.columns('z', 'y'):
  644. # ...
  645. Example of using the column objects from the statement itself::
  646. for z, y in result.columns(
  647. statement.selected_columns.c.z,
  648. statement.selected_columns.c.y
  649. ):
  650. # ...
  651. .. versionadded:: 1.4
  652. :param \*col_expressions: indicates columns to be returned. Elements
  653. may be integer row indexes, string column names, or appropriate
  654. :class:`.ColumnElement` objects corresponding to a select construct.
  655. :return: this :class:`_engine.Result` object with the modifications
  656. given.
  657. """
  658. return self._column_slices(col_expressions)
  659. def scalars(self, index=0):
  660. """Return a :class:`_result.ScalarResult` filtering object which
  661. will return single elements rather than :class:`_row.Row` objects.
  662. E.g.::
  663. >>> result = conn.execute(text("select int_id from table"))
  664. >>> result.scalars().all()
  665. [1, 2, 3]
  666. When results are fetched from the :class:`_result.ScalarResult`
  667. filtering object, the single column-row that would be returned by the
  668. :class:`_result.Result` is instead returned as the column's value.
  669. .. versionadded:: 1.4
  670. :param index: integer or row key indicating the column to be fetched
  671. from each row, defaults to ``0`` indicating the first column.
  672. :return: a new :class:`_result.ScalarResult` filtering object referring
  673. to this :class:`_result.Result` object.
  674. """
  675. return ScalarResult(self, index)
  676. def _getter(self, key, raiseerr=True):
  677. """return a callable that will retrieve the given key from a
  678. :class:`.Row`.
  679. """
  680. if self._source_supports_scalars:
  681. raise NotImplementedError(
  682. "can't use this function in 'only scalars' mode"
  683. )
  684. return self._metadata._getter(key, raiseerr)
  685. def _tuple_getter(self, keys):
  686. """return a callable that will retrieve the given keys from a
  687. :class:`.Row`.
  688. """
  689. if self._source_supports_scalars:
  690. raise NotImplementedError(
  691. "can't use this function in 'only scalars' mode"
  692. )
  693. return self._metadata._row_as_tuple_getter(keys)
  694. def mappings(self):
  695. """Apply a mappings filter to returned rows, returning an instance of
  696. :class:`_result.MappingResult`.
  697. When this filter is applied, fetching rows will return
  698. :class:`.RowMapping` objects instead of :class:`.Row` objects.
  699. .. versionadded:: 1.4
  700. :return: a new :class:`_result.MappingResult` filtering object
  701. referring to this :class:`_result.Result` object.
  702. """
  703. return MappingResult(self)
  704. def _raw_row_iterator(self):
  705. """Return a safe iterator that yields raw row data.
  706. This is used by the :meth:`._engine.Result.merge` method
  707. to merge multiple compatible results together.
  708. """
  709. raise NotImplementedError()
  710. def _fetchiter_impl(self):
  711. raise NotImplementedError()
  712. def _fetchone_impl(self, hard_close=False):
  713. raise NotImplementedError()
  714. def _fetchall_impl(self):
  715. raise NotImplementedError()
  716. def _fetchmany_impl(self, size=None):
  717. raise NotImplementedError()
  718. def __iter__(self):
  719. return self._iter_impl()
  720. def __next__(self):
  721. return self._next_impl()
  722. if py2k:
  723. def next(self): # noqa
  724. return self._next_impl()
  725. def partitions(self, size=None):
  726. """Iterate through sub-lists of rows of the size given.
  727. Each list will be of the size given, excluding the last list to
  728. be yielded, which may have a small number of rows. No empty
  729. lists will be yielded.
  730. The result object is automatically closed when the iterator
  731. is fully consumed.
  732. Note that the backend driver will usually buffer the entire result
  733. ahead of time unless the
  734. :paramref:`.Connection.execution_options.stream_results` execution
  735. option is used indicating that the driver should not pre-buffer
  736. results, if possible. Not all drivers support this option and
  737. the option is silently ignored for those who do not.
  738. .. versionadded:: 1.4
  739. :param size: indicate the maximum number of rows to be present
  740. in each list yielded. If None, makes use of the value set by
  741. :meth:`_engine.Result.yield_per`, if present, otherwise uses the
  742. :meth:`_engine.Result.fetchmany` default which may be backend
  743. specific.
  744. :return: iterator of lists
  745. """
  746. getter = self._manyrow_getter
  747. while True:
  748. partition = getter(self, size)
  749. if partition:
  750. yield partition
  751. else:
  752. break
  753. def fetchall(self):
  754. """A synonym for the :meth:`_engine.Result.all` method."""
  755. return self._allrows()
  756. def fetchone(self):
  757. """Fetch one row.
  758. When all rows are exhausted, returns None.
  759. This method is provided for backwards compatibility with
  760. SQLAlchemy 1.x.x.
  761. To fetch the first row of a result only, use the
  762. :meth:`_engine.Result.first` method. To iterate through all
  763. rows, iterate the :class:`_engine.Result` object directly.
  764. :return: a :class:`.Row` object if no filters are applied, or None
  765. if no rows remain.
  766. """
  767. row = self._onerow_getter(self)
  768. if row is _NO_ROW:
  769. return None
  770. else:
  771. return row
  772. def fetchmany(self, size=None):
  773. """Fetch many rows.
  774. When all rows are exhausted, returns an empty list.
  775. This method is provided for backwards compatibility with
  776. SQLAlchemy 1.x.x.
  777. To fetch rows in groups, use the :meth:`._result.Result.partitions`
  778. method.
  779. :return: a list of :class:`.Row` objects.
  780. """
  781. return self._manyrow_getter(self, size)
  782. def all(self):
  783. """Return all rows in a list.
  784. Closes the result set after invocation. Subsequent invocations
  785. will return an empty list.
  786. .. versionadded:: 1.4
  787. :return: a list of :class:`.Row` objects.
  788. """
  789. return self._allrows()
  790. def first(self):
  791. """Fetch the first row or None if no row is present.
  792. Closes the result set and discards remaining rows.
  793. .. note:: This method returns one **row**, e.g. tuple, by default.
  794. To return exactly one single scalar value, that is, the first
  795. column of the first row, use the :meth:`.Result.scalar` method,
  796. or combine :meth:`.Result.scalars` and :meth:`.Result.first`.
  797. :return: a :class:`.Row` object, or None
  798. if no rows remain.
  799. .. seealso::
  800. :meth:`_result.Result.scalar`
  801. :meth:`_result.Result.one`
  802. """
  803. return self._only_one_row(
  804. raise_for_second_row=False, raise_for_none=False, scalar=False
  805. )
  806. def one_or_none(self):
  807. """Return at most one result or raise an exception.
  808. Returns ``None`` if the result has no rows.
  809. Raises :class:`.MultipleResultsFound`
  810. if multiple rows are returned.
  811. .. versionadded:: 1.4
  812. :return: The first :class:`.Row` or None if no row is available.
  813. :raises: :class:`.MultipleResultsFound`
  814. .. seealso::
  815. :meth:`_result.Result.first`
  816. :meth:`_result.Result.one`
  817. """
  818. return self._only_one_row(
  819. raise_for_second_row=True, raise_for_none=False, scalar=False
  820. )
  821. def scalar_one(self):
  822. """Return exactly one scalar result or raise an exception.
  823. This is equivalent to calling :meth:`.Result.scalars` and then
  824. :meth:`.Result.one`.
  825. .. seealso::
  826. :meth:`.Result.one`
  827. :meth:`.Result.scalars`
  828. """
  829. return self._only_one_row(
  830. raise_for_second_row=True, raise_for_none=True, scalar=True
  831. )
  832. def scalar_one_or_none(self):
  833. """Return exactly one or no scalar result.
  834. This is equivalent to calling :meth:`.Result.scalars` and then
  835. :meth:`.Result.one_or_none`.
  836. .. seealso::
  837. :meth:`.Result.one_or_none`
  838. :meth:`.Result.scalars`
  839. """
  840. return self._only_one_row(
  841. raise_for_second_row=True, raise_for_none=False, scalar=True
  842. )
  843. def one(self):
  844. """Return exactly one row or raise an exception.
  845. Raises :class:`.NoResultFound` if the result returns no
  846. rows, or :class:`.MultipleResultsFound` if multiple rows
  847. would be returned.
  848. .. note:: This method returns one **row**, e.g. tuple, by default.
  849. To return exactly one single scalar value, that is, the first
  850. column of the first row, use the :meth:`.Result.scalar_one` method,
  851. or combine :meth:`.Result.scalars` and :meth:`.Result.one`.
  852. .. versionadded:: 1.4
  853. :return: The first :class:`.Row`.
  854. :raises: :class:`.MultipleResultsFound`, :class:`.NoResultFound`
  855. .. seealso::
  856. :meth:`_result.Result.first`
  857. :meth:`_result.Result.one_or_none`
  858. :meth:`_result.Result.scalar_one`
  859. """
  860. return self._only_one_row(
  861. raise_for_second_row=True, raise_for_none=True, scalar=False
  862. )
  863. def scalar(self):
  864. """Fetch the first column of the first row, and close the result set.
  865. Returns None if there are no rows to fetch.
  866. No validation is performed to test if additional rows remain.
  867. After calling this method, the object is fully closed,
  868. e.g. the :meth:`_engine.CursorResult.close`
  869. method will have been called.
  870. :return: a Python scalar value , or None if no rows remain.
  871. """
  872. return self._only_one_row(
  873. raise_for_second_row=False, raise_for_none=False, scalar=True
  874. )
  875. def freeze(self):
  876. """Return a callable object that will produce copies of this
  877. :class:`.Result` when invoked.
  878. The callable object returned is an instance of
  879. :class:`_engine.FrozenResult`.
  880. This is used for result set caching. The method must be called
  881. on the result when it has been unconsumed, and calling the method
  882. will consume the result fully. When the :class:`_engine.FrozenResult`
  883. is retrieved from a cache, it can be called any number of times where
  884. it will produce a new :class:`_engine.Result` object each time
  885. against its stored set of rows.
  886. .. seealso::
  887. :ref:`do_orm_execute_re_executing` - example usage within the
  888. ORM to implement a result-set cache.
  889. """
  890. return FrozenResult(self)
  891. def merge(self, *others):
  892. """Merge this :class:`.Result` with other compatible result
  893. objects.
  894. The object returned is an instance of :class:`_engine.MergedResult`,
  895. which will be composed of iterators from the given result
  896. objects.
  897. The new result will use the metadata from this result object.
  898. The subsequent result objects must be against an identical
  899. set of result / cursor metadata, otherwise the behavior is
  900. undefined.
  901. """
  902. return MergedResult(self._metadata, (self,) + others)
  903. class FilterResult(ResultInternal):
  904. """A wrapper for a :class:`_engine.Result` that returns objects other than
  905. :class:`_result.Row` objects, such as dictionaries or scalar objects.
  906. """
  907. _post_creational_filter = None
  908. def _soft_close(self, hard=False):
  909. self._real_result._soft_close(hard=hard)
  910. @property
  911. def _attributes(self):
  912. return self._real_result._attributes
  913. def _fetchiter_impl(self):
  914. return self._real_result._fetchiter_impl()
  915. def _fetchone_impl(self, hard_close=False):
  916. return self._real_result._fetchone_impl(hard_close=hard_close)
  917. def _fetchall_impl(self):
  918. return self._real_result._fetchall_impl()
  919. def _fetchmany_impl(self, size=None):
  920. return self._real_result._fetchmany_impl(size=size)
  921. class ScalarResult(FilterResult):
  922. """A wrapper for a :class:`_result.Result` that returns scalar values
  923. rather than :class:`_row.Row` values.
  924. The :class:`_result.ScalarResult` object is acquired by calling the
  925. :meth:`_result.Result.scalars` method.
  926. A special limitation of :class:`_result.ScalarResult` is that it has
  927. no ``fetchone()`` method; since the semantics of ``fetchone()`` are that
  928. the ``None`` value indicates no more results, this is not compatible
  929. with :class:`_result.ScalarResult` since there is no way to distinguish
  930. between ``None`` as a row value versus ``None`` as an indicator. Use
  931. ``next(result)`` to receive values individually.
  932. """
  933. _generate_rows = False
  934. def __init__(self, real_result, index):
  935. self._real_result = real_result
  936. if real_result._source_supports_scalars:
  937. self._metadata = real_result._metadata
  938. self._post_creational_filter = None
  939. else:
  940. self._metadata = real_result._metadata._reduce([index])
  941. self._post_creational_filter = operator.itemgetter(0)
  942. self._unique_filter_state = real_result._unique_filter_state
  943. def unique(self, strategy=None):
  944. """Apply unique filtering to the objects returned by this
  945. :class:`_engine.ScalarResult`.
  946. See :meth:`_engine.Result.unique` for usage details.
  947. """
  948. self._unique_filter_state = (set(), strategy)
  949. return self
  950. def partitions(self, size=None):
  951. """Iterate through sub-lists of elements of the size given.
  952. Equivalent to :meth:`_result.Result.partitions` except that
  953. scalar values, rather than :class:`_result.Row` objects,
  954. are returned.
  955. """
  956. getter = self._manyrow_getter
  957. while True:
  958. partition = getter(self, size)
  959. if partition:
  960. yield partition
  961. else:
  962. break
  963. def fetchall(self):
  964. """A synonym for the :meth:`_engine.ScalarResult.all` method."""
  965. return self._allrows()
  966. def fetchmany(self, size=None):
  967. """Fetch many objects.
  968. Equivalent to :meth:`_result.Result.fetchmany` except that
  969. scalar values, rather than :class:`_result.Row` objects,
  970. are returned.
  971. """
  972. return self._manyrow_getter(self, size)
  973. def all(self):
  974. """Return all scalar values in a list.
  975. Equivalent to :meth:`_result.Result.all` except that
  976. scalar values, rather than :class:`_result.Row` objects,
  977. are returned.
  978. """
  979. return self._allrows()
  980. def __iter__(self):
  981. return self._iter_impl()
  982. def __next__(self):
  983. return self._next_impl()
  984. if py2k:
  985. def next(self): # noqa
  986. return self._next_impl()
  987. def first(self):
  988. """Fetch the first object or None if no object is present.
  989. Equivalent to :meth:`_result.Result.first` except that
  990. scalar values, rather than :class:`_result.Row` objects,
  991. are returned.
  992. """
  993. return self._only_one_row(
  994. raise_for_second_row=False, raise_for_none=False, scalar=False
  995. )
  996. def one_or_none(self):
  997. """Return at most one object or raise an exception.
  998. Equivalent to :meth:`_result.Result.one_or_none` except that
  999. scalar values, rather than :class:`_result.Row` objects,
  1000. are returned.
  1001. """
  1002. return self._only_one_row(
  1003. raise_for_second_row=True, raise_for_none=False, scalar=False
  1004. )
  1005. def one(self):
  1006. """Return exactly one object or raise an exception.
  1007. Equivalent to :meth:`_result.Result.one` except that
  1008. scalar values, rather than :class:`_result.Row` objects,
  1009. are returned.
  1010. """
  1011. return self._only_one_row(
  1012. raise_for_second_row=True, raise_for_none=True, scalar=False
  1013. )
  1014. class MappingResult(_WithKeys, FilterResult):
  1015. """A wrapper for a :class:`_engine.Result` that returns dictionary values
  1016. rather than :class:`_engine.Row` values.
  1017. The :class:`_engine.MappingResult` object is acquired by calling the
  1018. :meth:`_engine.Result.mappings` method.
  1019. """
  1020. _generate_rows = True
  1021. _post_creational_filter = operator.attrgetter("_mapping")
  1022. def __init__(self, result):
  1023. self._real_result = result
  1024. self._unique_filter_state = result._unique_filter_state
  1025. self._metadata = result._metadata
  1026. if result._source_supports_scalars:
  1027. self._metadata = self._metadata._reduce([0])
  1028. def unique(self, strategy=None):
  1029. """Apply unique filtering to the objects returned by this
  1030. :class:`_engine.MappingResult`.
  1031. See :meth:`_engine.Result.unique` for usage details.
  1032. """
  1033. self._unique_filter_state = (set(), strategy)
  1034. return self
  1035. def columns(self, *col_expressions):
  1036. r"""Establish the columns that should be returned in each row."""
  1037. return self._column_slices(col_expressions)
  1038. def partitions(self, size=None):
  1039. """Iterate through sub-lists of elements of the size given.
  1040. Equivalent to :meth:`_result.Result.partitions` except that
  1041. mapping values, rather than :class:`_result.Row` objects,
  1042. are returned.
  1043. """
  1044. getter = self._manyrow_getter
  1045. while True:
  1046. partition = getter(self, size)
  1047. if partition:
  1048. yield partition
  1049. else:
  1050. break
  1051. def fetchall(self):
  1052. """A synonym for the :meth:`_engine.MappingResult.all` method."""
  1053. return self._allrows()
  1054. def fetchone(self):
  1055. """Fetch one object.
  1056. Equivalent to :meth:`_result.Result.fetchone` except that
  1057. mapping values, rather than :class:`_result.Row` objects,
  1058. are returned.
  1059. """
  1060. row = self._onerow_getter(self)
  1061. if row is _NO_ROW:
  1062. return None
  1063. else:
  1064. return row
  1065. def fetchmany(self, size=None):
  1066. """Fetch many objects.
  1067. Equivalent to :meth:`_result.Result.fetchmany` except that
  1068. mapping values, rather than :class:`_result.Row` objects,
  1069. are returned.
  1070. """
  1071. return self._manyrow_getter(self, size)
  1072. def all(self):
  1073. """Return all scalar values in a list.
  1074. Equivalent to :meth:`_result.Result.all` except that
  1075. mapping values, rather than :class:`_result.Row` objects,
  1076. are returned.
  1077. """
  1078. return self._allrows()
  1079. def __iter__(self):
  1080. return self._iter_impl()
  1081. def __next__(self):
  1082. return self._next_impl()
  1083. if py2k:
  1084. def next(self): # noqa
  1085. return self._next_impl()
  1086. def first(self):
  1087. """Fetch the first object or None if no object is present.
  1088. Equivalent to :meth:`_result.Result.first` except that
  1089. mapping values, rather than :class:`_result.Row` objects,
  1090. are returned.
  1091. """
  1092. return self._only_one_row(
  1093. raise_for_second_row=False, raise_for_none=False, scalar=False
  1094. )
  1095. def one_or_none(self):
  1096. """Return at most one object or raise an exception.
  1097. Equivalent to :meth:`_result.Result.one_or_none` except that
  1098. mapping values, rather than :class:`_result.Row` objects,
  1099. are returned.
  1100. """
  1101. return self._only_one_row(
  1102. raise_for_second_row=True, raise_for_none=False, scalar=False
  1103. )
  1104. def one(self):
  1105. """Return exactly one object or raise an exception.
  1106. Equivalent to :meth:`_result.Result.one` except that
  1107. mapping values, rather than :class:`_result.Row` objects,
  1108. are returned.
  1109. """
  1110. return self._only_one_row(
  1111. raise_for_second_row=True, raise_for_none=True, scalar=False
  1112. )
  1113. class FrozenResult(object):
  1114. """Represents a :class:`.Result` object in a "frozen" state suitable
  1115. for caching.
  1116. The :class:`_engine.FrozenResult` object is returned from the
  1117. :meth:`_engine.Result.freeze` method of any :class:`_engine.Result`
  1118. object.
  1119. A new iterable :class:`.Result` object is generated from a fixed
  1120. set of data each time the :class:`.FrozenResult` is invoked as
  1121. a callable::
  1122. result = connection.execute(query)
  1123. frozen = result.freeze()
  1124. unfrozen_result_one = frozen()
  1125. for row in unfrozen_result_one:
  1126. print(row)
  1127. unfrozen_result_two = frozen()
  1128. rows = unfrozen_result_two.all()
  1129. # ... etc
  1130. .. versionadded:: 1.4
  1131. .. seealso::
  1132. :ref:`do_orm_execute_re_executing` - example usage within the
  1133. ORM to implement a result-set cache.
  1134. :func:`_orm.loading.merge_frozen_result` - ORM function to merge
  1135. a frozen result back into a :class:`_orm.Session`.
  1136. """
  1137. def __init__(self, result):
  1138. self.metadata = result._metadata._for_freeze()
  1139. self._source_supports_scalars = result._source_supports_scalars
  1140. self._attributes = result._attributes
  1141. if self._source_supports_scalars:
  1142. self.data = list(result._raw_row_iterator())
  1143. else:
  1144. self.data = result.fetchall()
  1145. def rewrite_rows(self):
  1146. if self._source_supports_scalars:
  1147. return [[elem] for elem in self.data]
  1148. else:
  1149. return [list(row) for row in self.data]
  1150. def with_new_rows(self, tuple_data):
  1151. fr = FrozenResult.__new__(FrozenResult)
  1152. fr.metadata = self.metadata
  1153. fr._attributes = self._attributes
  1154. fr._source_supports_scalars = self._source_supports_scalars
  1155. if self._source_supports_scalars:
  1156. fr.data = [d[0] for d in tuple_data]
  1157. else:
  1158. fr.data = tuple_data
  1159. return fr
  1160. def __call__(self):
  1161. result = IteratorResult(self.metadata, iter(self.data))
  1162. result._attributes = self._attributes
  1163. result._source_supports_scalars = self._source_supports_scalars
  1164. return result
  1165. class IteratorResult(Result):
  1166. """A :class:`.Result` that gets data from a Python iterator of
  1167. :class:`.Row` objects.
  1168. .. versionadded:: 1.4
  1169. """
  1170. def __init__(
  1171. self,
  1172. cursor_metadata,
  1173. iterator,
  1174. raw=None,
  1175. _source_supports_scalars=False,
  1176. ):
  1177. self._metadata = cursor_metadata
  1178. self.iterator = iterator
  1179. self.raw = raw
  1180. self._source_supports_scalars = _source_supports_scalars
  1181. def _soft_close(self, **kw):
  1182. self.iterator = iter([])
  1183. def _raw_row_iterator(self):
  1184. return self.iterator
  1185. def _fetchiter_impl(self):
  1186. return self.iterator
  1187. def _fetchone_impl(self, hard_close=False):
  1188. row = next(self.iterator, _NO_ROW)
  1189. if row is _NO_ROW:
  1190. self._soft_close(hard=hard_close)
  1191. return None
  1192. else:
  1193. return row
  1194. def _fetchall_impl(self):
  1195. try:
  1196. return list(self.iterator)
  1197. finally:
  1198. self._soft_close()
  1199. def _fetchmany_impl(self, size=None):
  1200. return list(itertools.islice(self.iterator, 0, size))
  1201. def null_result():
  1202. return IteratorResult(SimpleResultMetaData([]), iter([]))
  1203. class ChunkedIteratorResult(IteratorResult):
  1204. """An :class:`.IteratorResult` that works from an iterator-producing callable.
  1205. The given ``chunks`` argument is a function that is given a number of rows
  1206. to return in each chunk, or ``None`` for all rows. The function should
  1207. then return an un-consumed iterator of lists, each list of the requested
  1208. size.
  1209. The function can be called at any time again, in which case it should
  1210. continue from the same result set but adjust the chunk size as given.
  1211. .. versionadded:: 1.4
  1212. """
  1213. def __init__(
  1214. self,
  1215. cursor_metadata,
  1216. chunks,
  1217. source_supports_scalars=False,
  1218. raw=None,
  1219. dynamic_yield_per=False,
  1220. ):
  1221. self._metadata = cursor_metadata
  1222. self.chunks = chunks
  1223. self._source_supports_scalars = source_supports_scalars
  1224. self.raw = raw
  1225. self.iterator = itertools.chain.from_iterable(self.chunks(None))
  1226. self.dynamic_yield_per = dynamic_yield_per
  1227. @_generative
  1228. def yield_per(self, num):
  1229. # TODO: this throws away the iterator which may be holding
  1230. # onto a chunk. the yield_per cannot be changed once any
  1231. # rows have been fetched. either find a way to enforce this,
  1232. # or we can't use itertools.chain and will instead have to
  1233. # keep track.
  1234. self._yield_per = num
  1235. self.iterator = itertools.chain.from_iterable(self.chunks(num))
  1236. def _fetchmany_impl(self, size=None):
  1237. if self.dynamic_yield_per:
  1238. self.iterator = itertools.chain.from_iterable(self.chunks(size))
  1239. return super(ChunkedIteratorResult, self)._fetchmany_impl(size=size)
  1240. class MergedResult(IteratorResult):
  1241. """A :class:`_engine.Result` that is merged from any number of
  1242. :class:`_engine.Result` objects.
  1243. Returned by the :meth:`_engine.Result.merge` method.
  1244. .. versionadded:: 1.4
  1245. """
  1246. closed = False
  1247. def __init__(self, cursor_metadata, results):
  1248. self._results = results
  1249. super(MergedResult, self).__init__(
  1250. cursor_metadata,
  1251. itertools.chain.from_iterable(
  1252. r._raw_row_iterator() for r in results
  1253. ),
  1254. )
  1255. self._unique_filter_state = results[0]._unique_filter_state
  1256. self._yield_per = results[0]._yield_per
  1257. # going to try something w/ this in next rev
  1258. self._source_supports_scalars = results[0]._source_supports_scalars
  1259. self._attributes = self._attributes.merge_with(
  1260. *[r._attributes for r in results]
  1261. )
  1262. def close(self):
  1263. self._soft_close(hard=True)
  1264. def _soft_close(self, hard=False):
  1265. for r in self._results:
  1266. r._soft_close(hard=hard)
  1267. if hard:
  1268. self.closed = True