result.py 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915
  1. # engine/result.py
  2. # Copyright (C) 2005-2024 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: https://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 if extras else ()),
  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. _is_cursor = False
  228. @HasMemoized.memoized_attribute
  229. def _row_getter(self):
  230. real_result = self._real_result if self._real_result else self
  231. if real_result._source_supports_scalars:
  232. if not self._generate_rows:
  233. return None
  234. else:
  235. _proc = real_result._process_row
  236. def process_row(
  237. metadata, processors, keymap, key_style, scalar_obj
  238. ):
  239. return _proc(
  240. metadata, processors, keymap, key_style, (scalar_obj,)
  241. )
  242. else:
  243. process_row = real_result._process_row
  244. key_style = real_result._process_row._default_key_style
  245. metadata = self._metadata
  246. keymap = metadata._keymap
  247. processors = metadata._processors
  248. tf = metadata._tuplefilter
  249. if tf and not real_result._source_supports_scalars:
  250. if processors:
  251. processors = tf(processors)
  252. _make_row_orig = functools.partial(
  253. process_row, metadata, processors, keymap, key_style
  254. )
  255. def make_row(row):
  256. return _make_row_orig(tf(row))
  257. else:
  258. make_row = functools.partial(
  259. process_row, metadata, processors, keymap, key_style
  260. )
  261. fns = ()
  262. if real_result._row_logging_fn:
  263. fns = (real_result._row_logging_fn,)
  264. else:
  265. fns = ()
  266. if fns:
  267. _make_row = make_row
  268. def make_row(row):
  269. row = _make_row(row)
  270. for fn in fns:
  271. row = fn(row)
  272. return row
  273. return make_row
  274. @HasMemoized.memoized_attribute
  275. def _iterator_getter(self):
  276. make_row = self._row_getter
  277. post_creational_filter = self._post_creational_filter
  278. if self._unique_filter_state:
  279. uniques, strategy = self._unique_strategy
  280. def iterrows(self):
  281. for row in self._fetchiter_impl():
  282. obj = make_row(row) if make_row else row
  283. hashed = strategy(obj) if strategy else obj
  284. if hashed in uniques:
  285. continue
  286. uniques.add(hashed)
  287. if post_creational_filter:
  288. obj = post_creational_filter(obj)
  289. yield obj
  290. else:
  291. def iterrows(self):
  292. for row in self._fetchiter_impl():
  293. row = make_row(row) if make_row else row
  294. if post_creational_filter:
  295. row = post_creational_filter(row)
  296. yield row
  297. return iterrows
  298. def _raw_all_rows(self):
  299. make_row = self._row_getter
  300. rows = self._fetchall_impl()
  301. return [make_row(row) for row in rows]
  302. def _allrows(self):
  303. post_creational_filter = self._post_creational_filter
  304. make_row = self._row_getter
  305. rows = self._fetchall_impl()
  306. if make_row:
  307. made_rows = [make_row(row) for row in rows]
  308. else:
  309. made_rows = rows
  310. if self._unique_filter_state:
  311. uniques, strategy = self._unique_strategy
  312. rows = [
  313. made_row
  314. for made_row, sig_row in [
  315. (
  316. made_row,
  317. strategy(made_row) if strategy else made_row,
  318. )
  319. for made_row in made_rows
  320. ]
  321. if sig_row not in uniques and not uniques.add(sig_row)
  322. ]
  323. else:
  324. rows = made_rows
  325. if post_creational_filter:
  326. rows = [post_creational_filter(row) for row in rows]
  327. return rows
  328. @HasMemoized.memoized_attribute
  329. def _onerow_getter(self):
  330. make_row = self._row_getter
  331. post_creational_filter = self._post_creational_filter
  332. if self._unique_filter_state:
  333. uniques, strategy = self._unique_strategy
  334. def onerow(self):
  335. _onerow = self._fetchone_impl
  336. while True:
  337. row = _onerow()
  338. if row is None:
  339. return _NO_ROW
  340. else:
  341. obj = make_row(row) if make_row else row
  342. hashed = strategy(obj) if strategy else obj
  343. if hashed in uniques:
  344. continue
  345. else:
  346. uniques.add(hashed)
  347. if post_creational_filter:
  348. obj = post_creational_filter(obj)
  349. return obj
  350. else:
  351. def onerow(self):
  352. row = self._fetchone_impl()
  353. if row is None:
  354. return _NO_ROW
  355. else:
  356. row = make_row(row) if make_row else row
  357. if post_creational_filter:
  358. row = post_creational_filter(row)
  359. return row
  360. return onerow
  361. @HasMemoized.memoized_attribute
  362. def _manyrow_getter(self):
  363. make_row = self._row_getter
  364. post_creational_filter = self._post_creational_filter
  365. if self._unique_filter_state:
  366. uniques, strategy = self._unique_strategy
  367. def filterrows(make_row, rows, strategy, uniques):
  368. if make_row:
  369. rows = [make_row(row) for row in rows]
  370. if strategy:
  371. made_rows = (
  372. (made_row, strategy(made_row)) for made_row in rows
  373. )
  374. else:
  375. made_rows = ((made_row, made_row) for made_row in rows)
  376. return [
  377. made_row
  378. for made_row, sig_row in made_rows
  379. if sig_row not in uniques and not uniques.add(sig_row)
  380. ]
  381. def manyrows(self, num):
  382. collect = []
  383. _manyrows = self._fetchmany_impl
  384. if num is None:
  385. # if None is passed, we don't know the default
  386. # manyrows number, DBAPI has this as cursor.arraysize
  387. # different DBAPIs / fetch strategies may be different.
  388. # do a fetch to find what the number is. if there are
  389. # only fewer rows left, then it doesn't matter.
  390. real_result = (
  391. self._real_result if self._real_result else self
  392. )
  393. if real_result._yield_per:
  394. num_required = num = real_result._yield_per
  395. else:
  396. rows = _manyrows(num)
  397. num = len(rows)
  398. collect.extend(
  399. filterrows(make_row, rows, strategy, uniques)
  400. )
  401. num_required = num - len(collect)
  402. else:
  403. num_required = num
  404. while num_required:
  405. rows = _manyrows(num_required)
  406. if not rows:
  407. break
  408. collect.extend(
  409. filterrows(make_row, rows, strategy, uniques)
  410. )
  411. num_required = num - len(collect)
  412. if post_creational_filter:
  413. collect = [post_creational_filter(row) for row in collect]
  414. return collect
  415. else:
  416. def manyrows(self, num):
  417. if num is None:
  418. real_result = (
  419. self._real_result if self._real_result else self
  420. )
  421. num = real_result._yield_per
  422. rows = self._fetchmany_impl(num)
  423. if make_row:
  424. rows = [make_row(row) for row in rows]
  425. if post_creational_filter:
  426. rows = [post_creational_filter(row) for row in rows]
  427. return rows
  428. return manyrows
  429. def _only_one_row(
  430. self,
  431. raise_for_second_row,
  432. raise_for_none,
  433. scalar,
  434. ):
  435. onerow = self._fetchone_impl
  436. row = onerow(hard_close=True)
  437. if row is None:
  438. if raise_for_none:
  439. raise exc.NoResultFound(
  440. "No row was found when one was required"
  441. )
  442. else:
  443. return None
  444. if scalar and self._source_supports_scalars:
  445. self._generate_rows = False
  446. make_row = None
  447. else:
  448. make_row = self._row_getter
  449. try:
  450. row = make_row(row) if make_row else row
  451. except:
  452. self._soft_close(hard=True)
  453. raise
  454. if raise_for_second_row:
  455. if self._unique_filter_state:
  456. # for no second row but uniqueness, need to essentially
  457. # consume the entire result :(
  458. uniques, strategy = self._unique_strategy
  459. existing_row_hash = strategy(row) if strategy else row
  460. while True:
  461. next_row = onerow(hard_close=True)
  462. if next_row is None:
  463. next_row = _NO_ROW
  464. break
  465. try:
  466. next_row = make_row(next_row) if make_row else next_row
  467. if strategy:
  468. if existing_row_hash == strategy(next_row):
  469. continue
  470. elif row == next_row:
  471. continue
  472. # here, we have a row and it's different
  473. break
  474. except:
  475. self._soft_close(hard=True)
  476. raise
  477. else:
  478. next_row = onerow(hard_close=True)
  479. if next_row is None:
  480. next_row = _NO_ROW
  481. if next_row is not _NO_ROW:
  482. self._soft_close(hard=True)
  483. raise exc.MultipleResultsFound(
  484. "Multiple rows were found when exactly one was required"
  485. if raise_for_none
  486. else "Multiple rows were found when one or none "
  487. "was required"
  488. )
  489. else:
  490. next_row = _NO_ROW
  491. # if we checked for second row then that would have
  492. # closed us :)
  493. self._soft_close(hard=True)
  494. if not scalar:
  495. post_creational_filter = self._post_creational_filter
  496. if post_creational_filter:
  497. row = post_creational_filter(row)
  498. if scalar and make_row:
  499. return row[0]
  500. else:
  501. return row
  502. def _iter_impl(self):
  503. return self._iterator_getter(self)
  504. def _next_impl(self):
  505. row = self._onerow_getter(self)
  506. if row is _NO_ROW:
  507. raise StopIteration()
  508. else:
  509. return row
  510. @_generative
  511. def _column_slices(self, indexes):
  512. real_result = self._real_result if self._real_result else self
  513. if real_result._source_supports_scalars and len(indexes) == 1:
  514. util.warn_deprecated(
  515. "The Result.columns() method has a bug in SQLAlchemy 1.4 that "
  516. "is causing it to yield scalar values, rather than Row "
  517. "objects, in the case where a single index is passed and the "
  518. "result is against ORM mapped objects. In SQLAlchemy 2.0, "
  519. "Result will continue yield Row objects in this scenario. "
  520. "Use the Result.scalars() method to yield scalar values.",
  521. "2.0",
  522. )
  523. self._generate_rows = False
  524. else:
  525. self._generate_rows = True
  526. self._metadata = self._metadata._reduce(indexes)
  527. @HasMemoized.memoized_attribute
  528. def _unique_strategy(self):
  529. uniques, strategy = self._unique_filter_state
  530. real_result = (
  531. self._real_result if self._real_result is not None else self
  532. )
  533. if not strategy and self._metadata._unique_filters:
  534. if (
  535. real_result._source_supports_scalars
  536. and not self._generate_rows
  537. ):
  538. strategy = self._metadata._unique_filters[0]
  539. else:
  540. filters = self._metadata._unique_filters
  541. if self._metadata._tuplefilter:
  542. filters = self._metadata._tuplefilter(filters)
  543. strategy = operator.methodcaller("_filter_on_values", filters)
  544. return uniques, strategy
  545. class _WithKeys(object):
  546. # used mainly to share documentation on the keys method.
  547. # py2k does not allow overriding the __doc__ attribute.
  548. def keys(self):
  549. """Return an iterable view which yields the string keys that would
  550. be represented by each :class:`_engine.Row`.
  551. The keys can represent the labels of the columns returned by a core
  552. statement or the names of the orm classes returned by an orm
  553. execution.
  554. The view also can be tested for key containment using the Python
  555. ``in`` operator, which will test both for the string keys represented
  556. in the view, as well as for alternate keys such as column objects.
  557. .. versionchanged:: 1.4 a key view object is returned rather than a
  558. plain list.
  559. """
  560. return self._metadata.keys
  561. class Result(_WithKeys, ResultInternal):
  562. """Represent a set of database results.
  563. .. versionadded:: 1.4 The :class:`_engine.Result` object provides a
  564. completely updated usage model and calling facade for SQLAlchemy
  565. Core and SQLAlchemy ORM. In Core, it forms the basis of the
  566. :class:`_engine.CursorResult` object which replaces the previous
  567. :class:`_engine.ResultProxy` interface. When using the ORM, a
  568. higher level object called :class:`_engine.ChunkedIteratorResult`
  569. is normally used.
  570. .. note:: In SQLAlchemy 1.4 and above, this object is
  571. used for ORM results returned by :meth:`_orm.Session.execute`, which can
  572. yield instances of ORM mapped objects either individually or within
  573. tuple-like rows. Note that the :class:`_engine.Result` object does not
  574. deduplicate instances or rows automatically as is the case with the
  575. legacy :class:`_orm.Query` object. For in-Python de-duplication of
  576. instances or rows, use the :meth:`_engine.Result.unique` modifier
  577. method.
  578. .. seealso::
  579. :ref:`tutorial_fetching_rows` - in the :doc:`/tutorial/index`
  580. """
  581. _process_row = Row
  582. _row_logging_fn = None
  583. _source_supports_scalars = False
  584. _yield_per = None
  585. _attributes = util.immutabledict()
  586. def __init__(self, cursor_metadata):
  587. self._metadata = cursor_metadata
  588. def _soft_close(self, hard=False):
  589. raise NotImplementedError()
  590. def close(self):
  591. """close this :class:`_engine.Result`.
  592. The behavior of this method is implementation specific, and is
  593. not implemented by default. The method should generally end
  594. the resources in use by the result object and also cause any
  595. subsequent iteration or row fetching to raise
  596. :class:`.ResourceClosedError`.
  597. .. versionadded:: 1.4.27 - ``.close()`` was previously not generally
  598. available for all :class:`_engine.Result` classes, instead only
  599. being available on the :class:`_engine.CursorResult` returned for
  600. Core statement executions. As most other result objects, namely the
  601. ones used by the ORM, are proxying a :class:`_engine.CursorResult`
  602. in any case, this allows the underlying cursor result to be closed
  603. from the outside facade for the case when the ORM query is using
  604. the ``yield_per`` execution option where it does not immediately
  605. exhaust and autoclose the database cursor.
  606. """
  607. self._soft_close(hard=True)
  608. @property
  609. def _soft_closed(self):
  610. raise NotImplementedError()
  611. @property
  612. def closed(self):
  613. """return ``True`` if this :class:`_engine.Result` reports .closed
  614. .. versionadded:: 1.4.43
  615. """
  616. raise NotImplementedError()
  617. @_generative
  618. def yield_per(self, num):
  619. """Configure the row-fetching strategy to fetch ``num`` rows at a time.
  620. This impacts the underlying behavior of the result when iterating over
  621. the result object, or otherwise making use of methods such as
  622. :meth:`_engine.Result.fetchone` that return one row at a time. Data
  623. from the underlying cursor or other data source will be buffered up to
  624. this many rows in memory, and the buffered collection will then be
  625. yielded out one row at at time or as many rows are requested. Each time
  626. the buffer clears, it will be refreshed to this many rows or as many
  627. rows remain if fewer remain.
  628. The :meth:`_engine.Result.yield_per` method is generally used in
  629. conjunction with the
  630. :paramref:`_engine.Connection.execution_options.stream_results`
  631. execution option, which will allow the database dialect in use to make
  632. use of a server side cursor, if the DBAPI supports a specific "server
  633. side cursor" mode separate from its default mode of operation.
  634. .. tip::
  635. Consider using the
  636. :paramref:`_engine.Connection.execution_options.yield_per`
  637. execution option, which will simultaneously set
  638. :paramref:`_engine.Connection.execution_options.stream_results`
  639. to ensure the use of server side cursors, as well as automatically
  640. invoke the :meth:`_engine.Result.yield_per` method to establish
  641. a fixed row buffer size at once.
  642. The :paramref:`_engine.Connection.execution_options.yield_per`
  643. execution option is available for ORM operations, with
  644. :class:`_orm.Session`-oriented use described at
  645. :ref:`orm_queryguide_yield_per`. The Core-only version which works
  646. with :class:`_engine.Connection` is new as of SQLAlchemy 1.4.40.
  647. .. versionadded:: 1.4
  648. :param num: number of rows to fetch each time the buffer is refilled.
  649. If set to a value below 1, fetches all rows for the next buffer.
  650. .. seealso::
  651. :ref:`engine_stream_results` - describes Core behavior for
  652. :meth:`_engine.Result.yield_per`
  653. :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel`
  654. """
  655. self._yield_per = num
  656. @_generative
  657. def unique(self, strategy=None):
  658. """Apply unique filtering to the objects returned by this
  659. :class:`_engine.Result`.
  660. When this filter is applied with no arguments, the rows or objects
  661. returned will filtered such that each row is returned uniquely. The
  662. algorithm used to determine this uniqueness is by default the Python
  663. hashing identity of the whole tuple. In some cases a specialized
  664. per-entity hashing scheme may be used, such as when using the ORM, a
  665. scheme is applied which works against the primary key identity of
  666. returned objects.
  667. The unique filter is applied **after all other filters**, which means
  668. if the columns returned have been refined using a method such as the
  669. :meth:`_engine.Result.columns` or :meth:`_engine.Result.scalars`
  670. method, the uniquing is applied to **only the column or columns
  671. returned**. This occurs regardless of the order in which these
  672. methods have been called upon the :class:`_engine.Result` object.
  673. The unique filter also changes the calculus used for methods like
  674. :meth:`_engine.Result.fetchmany` and :meth:`_engine.Result.partitions`.
  675. When using :meth:`_engine.Result.unique`, these methods will continue
  676. to yield the number of rows or objects requested, after uniquing
  677. has been applied. However, this necessarily impacts the buffering
  678. behavior of the underlying cursor or datasource, such that multiple
  679. underlying calls to ``cursor.fetchmany()`` may be necessary in order
  680. to accumulate enough objects in order to provide a unique collection
  681. of the requested size.
  682. :param strategy: a callable that will be applied to rows or objects
  683. being iterated, which should return an object that represents the
  684. unique value of the row. A Python ``set()`` is used to store
  685. these identities. If not passed, a default uniqueness strategy
  686. is used which may have been assembled by the source of this
  687. :class:`_engine.Result` object.
  688. """
  689. self._unique_filter_state = (set(), strategy)
  690. def columns(self, *col_expressions):
  691. r"""Establish the columns that should be returned in each row.
  692. This method may be used to limit the columns returned as well
  693. as to reorder them. The given list of expressions are normally
  694. a series of integers or string key names. They may also be
  695. appropriate :class:`.ColumnElement` objects which correspond to
  696. a given statement construct.
  697. E.g.::
  698. statement = select(table.c.x, table.c.y, table.c.z)
  699. result = connection.execute(statement)
  700. for z, y in result.columns('z', 'y'):
  701. # ...
  702. Example of using the column objects from the statement itself::
  703. for z, y in result.columns(
  704. statement.selected_columns.c.z,
  705. statement.selected_columns.c.y
  706. ):
  707. # ...
  708. .. versionadded:: 1.4
  709. :param \*col_expressions: indicates columns to be returned. Elements
  710. may be integer row indexes, string column names, or appropriate
  711. :class:`.ColumnElement` objects corresponding to a select construct.
  712. :return: this :class:`_engine.Result` object with the modifications
  713. given.
  714. """
  715. return self._column_slices(col_expressions)
  716. def scalars(self, index=0):
  717. """Return a :class:`_engine.ScalarResult` filtering object which
  718. will return single elements rather than :class:`_row.Row` objects.
  719. E.g.::
  720. >>> result = conn.execute(text("select int_id from table"))
  721. >>> result.scalars().all()
  722. [1, 2, 3]
  723. When results are fetched from the :class:`_engine.ScalarResult`
  724. filtering object, the single column-row that would be returned by the
  725. :class:`_engine.Result` is instead returned as the column's value.
  726. .. versionadded:: 1.4
  727. :param index: integer or row key indicating the column to be fetched
  728. from each row, defaults to ``0`` indicating the first column.
  729. :return: a new :class:`_engine.ScalarResult` filtering object referring
  730. to this :class:`_engine.Result` object.
  731. """
  732. return ScalarResult(self, index)
  733. def _getter(self, key, raiseerr=True):
  734. """return a callable that will retrieve the given key from a
  735. :class:`_engine.Row`.
  736. """
  737. if self._source_supports_scalars:
  738. raise NotImplementedError(
  739. "can't use this function in 'only scalars' mode"
  740. )
  741. return self._metadata._getter(key, raiseerr)
  742. def _tuple_getter(self, keys):
  743. """return a callable that will retrieve the given keys from a
  744. :class:`_engine.Row`.
  745. """
  746. if self._source_supports_scalars:
  747. raise NotImplementedError(
  748. "can't use this function in 'only scalars' mode"
  749. )
  750. return self._metadata._row_as_tuple_getter(keys)
  751. def mappings(self):
  752. """Apply a mappings filter to returned rows, returning an instance of
  753. :class:`_engine.MappingResult`.
  754. When this filter is applied, fetching rows will return
  755. :class:`_engine.RowMapping` objects instead of :class:`_engine.Row`
  756. objects.
  757. .. versionadded:: 1.4
  758. :return: a new :class:`_engine.MappingResult` filtering object
  759. referring to this :class:`_engine.Result` object.
  760. """
  761. return MappingResult(self)
  762. def _raw_row_iterator(self):
  763. """Return a safe iterator that yields raw row data.
  764. This is used by the :meth:`_engine.Result.merge` method
  765. to merge multiple compatible results together.
  766. """
  767. raise NotImplementedError()
  768. def _fetchiter_impl(self):
  769. raise NotImplementedError()
  770. def _fetchone_impl(self, hard_close=False):
  771. raise NotImplementedError()
  772. def _fetchall_impl(self):
  773. raise NotImplementedError()
  774. def _fetchmany_impl(self, size=None):
  775. raise NotImplementedError()
  776. def __iter__(self):
  777. return self._iter_impl()
  778. def __next__(self):
  779. return self._next_impl()
  780. if py2k:
  781. def next(self): # noqa
  782. return self._next_impl()
  783. def partitions(self, size=None):
  784. """Iterate through sub-lists of rows of the size given.
  785. Each list will be of the size given, excluding the last list to
  786. be yielded, which may have a small number of rows. No empty
  787. lists will be yielded.
  788. The result object is automatically closed when the iterator
  789. is fully consumed.
  790. Note that the backend driver will usually buffer the entire result
  791. ahead of time unless the
  792. :paramref:`.Connection.execution_options.stream_results` execution
  793. option is used indicating that the driver should not pre-buffer
  794. results, if possible. Not all drivers support this option and
  795. the option is silently ignored for those who do not.
  796. When using the ORM, the :meth:`_engine.Result.partitions` method
  797. is typically more effective from a memory perspective when it is
  798. combined with use of the
  799. :ref:`yield_per execution option <orm_queryguide_yield_per>`,
  800. which instructs both the DBAPI driver to use server side cursors,
  801. if available, as well as instructs the ORM loading internals to only
  802. build a certain amount of ORM objects from a result at a time before
  803. yielding them out.
  804. .. versionadded:: 1.4
  805. :param size: indicate the maximum number of rows to be present
  806. in each list yielded. If None, makes use of the value set by
  807. the :meth:`_engine.Result.yield_per`, method, if it were called,
  808. or the :paramref:`_engine.Connection.execution_options.yield_per`
  809. execution option, which is equivalent in this regard. If
  810. yield_per weren't set, it makes use of the
  811. :meth:`_engine.Result.fetchmany` default, which may be backend
  812. specific and not well defined.
  813. :return: iterator of lists
  814. .. seealso::
  815. :ref:`engine_stream_results`
  816. :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel`
  817. """
  818. getter = self._manyrow_getter
  819. while True:
  820. partition = getter(self, size)
  821. if partition:
  822. yield partition
  823. else:
  824. break
  825. def fetchall(self):
  826. """A synonym for the :meth:`_engine.Result.all` method."""
  827. return self._allrows()
  828. def fetchone(self):
  829. """Fetch one row.
  830. When all rows are exhausted, returns None.
  831. This method is provided for backwards compatibility with
  832. SQLAlchemy 1.x.x.
  833. To fetch the first row of a result only, use the
  834. :meth:`_engine.Result.first` method. To iterate through all
  835. rows, iterate the :class:`_engine.Result` object directly.
  836. :return: a :class:`_engine.Row` object if no filters are applied,
  837. or ``None`` if no rows remain.
  838. """
  839. row = self._onerow_getter(self)
  840. if row is _NO_ROW:
  841. return None
  842. else:
  843. return row
  844. def fetchmany(self, size=None):
  845. """Fetch many rows.
  846. When all rows are exhausted, returns an empty list.
  847. This method is provided for backwards compatibility with
  848. SQLAlchemy 1.x.x.
  849. To fetch rows in groups, use the :meth:`_engine.Result.partitions`
  850. method.
  851. :return: a list of :class:`_engine.Row` objects.
  852. .. seealso::
  853. :meth:`_engine.Result.partitions`
  854. """
  855. return self._manyrow_getter(self, size)
  856. def all(self):
  857. """Return all rows in a list.
  858. Closes the result set after invocation. Subsequent invocations
  859. will return an empty list.
  860. .. versionadded:: 1.4
  861. :return: a list of :class:`_engine.Row` objects.
  862. """
  863. return self._allrows()
  864. def first(self):
  865. """Fetch the first row or ``None`` if no row is present.
  866. Closes the result set and discards remaining rows.
  867. .. note:: This method returns one **row**, e.g. tuple, by default.
  868. To return exactly one single scalar value, that is, the first
  869. column of the first row, use the
  870. :meth:`_engine.Result.scalar` method,
  871. or combine :meth:`_engine.Result.scalars` and
  872. :meth:`_engine.Result.first`.
  873. Additionally, in contrast to the behavior of the legacy ORM
  874. :meth:`_orm.Query.first` method, **no limit is applied** to the
  875. SQL query which was invoked to produce this
  876. :class:`_engine.Result`;
  877. for a DBAPI driver that buffers results in memory before yielding
  878. rows, all rows will be sent to the Python process and all but
  879. the first row will be discarded.
  880. .. seealso::
  881. :ref:`migration_20_unify_select`
  882. :return: a :class:`_engine.Row` object, or None
  883. if no rows remain.
  884. .. seealso::
  885. :meth:`_engine.Result.scalar`
  886. :meth:`_engine.Result.one`
  887. """
  888. return self._only_one_row(
  889. raise_for_second_row=False, raise_for_none=False, scalar=False
  890. )
  891. def one_or_none(self):
  892. """Return at most one result or raise an exception.
  893. Returns ``None`` if the result has no rows.
  894. Raises :class:`.MultipleResultsFound`
  895. if multiple rows are returned.
  896. .. versionadded:: 1.4
  897. :return: The first :class:`_engine.Row` or ``None`` if no row
  898. is available.
  899. :raises: :class:`.MultipleResultsFound`
  900. .. seealso::
  901. :meth:`_engine.Result.first`
  902. :meth:`_engine.Result.one`
  903. """
  904. return self._only_one_row(
  905. raise_for_second_row=True, raise_for_none=False, scalar=False
  906. )
  907. def scalar_one(self):
  908. """Return exactly one scalar result or raise an exception.
  909. This is equivalent to calling :meth:`_engine.Result.scalars` and
  910. then :meth:`_engine.Result.one`.
  911. .. seealso::
  912. :meth:`_engine.Result.one`
  913. :meth:`_engine.Result.scalars`
  914. """
  915. return self._only_one_row(
  916. raise_for_second_row=True, raise_for_none=True, scalar=True
  917. )
  918. def scalar_one_or_none(self):
  919. """Return exactly one scalar result or ``None``.
  920. This is equivalent to calling :meth:`_engine.Result.scalars` and
  921. then :meth:`_engine.Result.one_or_none`.
  922. .. seealso::
  923. :meth:`_engine.Result.one_or_none`
  924. :meth:`_engine.Result.scalars`
  925. """
  926. return self._only_one_row(
  927. raise_for_second_row=True, raise_for_none=False, scalar=True
  928. )
  929. def one(self):
  930. """Return exactly one row or raise an exception.
  931. Raises :class:`.NoResultFound` if the result returns no
  932. rows, or :class:`.MultipleResultsFound` if multiple rows
  933. would be returned.
  934. .. note:: This method returns one **row**, e.g. tuple, by default.
  935. To return exactly one single scalar value, that is, the first
  936. column of the first row, use the
  937. :meth:`_engine.Result.scalar_one` method, or combine
  938. :meth:`_engine.Result.scalars` and
  939. :meth:`_engine.Result.one`.
  940. .. versionadded:: 1.4
  941. :return: The first :class:`_engine.Row`.
  942. :raises: :class:`.MultipleResultsFound`, :class:`.NoResultFound`
  943. .. seealso::
  944. :meth:`_engine.Result.first`
  945. :meth:`_engine.Result.one_or_none`
  946. :meth:`_engine.Result.scalar_one`
  947. """
  948. return self._only_one_row(
  949. raise_for_second_row=True, raise_for_none=True, scalar=False
  950. )
  951. def scalar(self):
  952. """Fetch the first column of the first row, and close the result set.
  953. Returns ``None`` if there are no rows to fetch.
  954. No validation is performed to test if additional rows remain.
  955. After calling this method, the object is fully closed,
  956. e.g. the :meth:`_engine.CursorResult.close`
  957. method will have been called.
  958. :return: a Python scalar value, or ``None`` if no rows remain.
  959. """
  960. return self._only_one_row(
  961. raise_for_second_row=False, raise_for_none=False, scalar=True
  962. )
  963. def freeze(self):
  964. """Return a callable object that will produce copies of this
  965. :class:`_engine.Result` when invoked.
  966. The callable object returned is an instance of
  967. :class:`_engine.FrozenResult`.
  968. This is used for result set caching. The method must be called
  969. on the result when it has been unconsumed, and calling the method
  970. will consume the result fully. When the :class:`_engine.FrozenResult`
  971. is retrieved from a cache, it can be called any number of times where
  972. it will produce a new :class:`_engine.Result` object each time
  973. against its stored set of rows.
  974. .. seealso::
  975. :ref:`do_orm_execute_re_executing` - example usage within the
  976. ORM to implement a result-set cache.
  977. """
  978. return FrozenResult(self)
  979. def merge(self, *others):
  980. """Merge this :class:`_engine.Result` with other compatible result
  981. objects.
  982. The object returned is an instance of :class:`_engine.MergedResult`,
  983. which will be composed of iterators from the given result
  984. objects.
  985. The new result will use the metadata from this result object.
  986. The subsequent result objects must be against an identical
  987. set of result / cursor metadata, otherwise the behavior is
  988. undefined.
  989. """
  990. return MergedResult(self._metadata, (self,) + others)
  991. class FilterResult(ResultInternal):
  992. """A wrapper for a :class:`_engine.Result` that returns objects other than
  993. :class:`_engine.Row` objects, such as dictionaries or scalar objects.
  994. :class:`_engine.FilterResult` is the common base for additional result
  995. APIs including :class:`_engine.MappingResult`,
  996. :class:`_engine.ScalarResult` and :class:`_engine.AsyncResult`.
  997. """
  998. _post_creational_filter = None
  999. @_generative
  1000. def yield_per(self, num):
  1001. """Configure the row-fetching strategy to fetch ``num`` rows at a time.
  1002. The :meth:`_engine.FilterResult.yield_per` method is a pass through
  1003. to the :meth:`_engine.Result.yield_per` method. See that method's
  1004. documentation for usage notes.
  1005. .. versionadded:: 1.4.40 - added :meth:`_engine.FilterResult.yield_per`
  1006. so that the method is available on all result set implementations
  1007. .. seealso::
  1008. :ref:`engine_stream_results` - describes Core behavior for
  1009. :meth:`_engine.Result.yield_per`
  1010. :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel`
  1011. """
  1012. self._real_result = self._real_result.yield_per(num)
  1013. def _soft_close(self, hard=False):
  1014. self._real_result._soft_close(hard=hard)
  1015. @property
  1016. def _soft_closed(self):
  1017. return self._real_result._soft_closed
  1018. @property
  1019. def closed(self):
  1020. """Return ``True`` if the underlying :class:`_engine.Result` reports
  1021. closed
  1022. .. versionadded:: 1.4.43
  1023. """
  1024. return self._real_result.closed # type: ignore
  1025. def close(self):
  1026. """Close this :class:`_engine.FilterResult`.
  1027. .. versionadded:: 1.4.43
  1028. """
  1029. self._real_result.close()
  1030. @property
  1031. def _attributes(self):
  1032. return self._real_result._attributes
  1033. def _fetchiter_impl(self):
  1034. return self._real_result._fetchiter_impl()
  1035. def _fetchone_impl(self, hard_close=False):
  1036. return self._real_result._fetchone_impl(hard_close=hard_close)
  1037. def _fetchall_impl(self):
  1038. return self._real_result._fetchall_impl()
  1039. def _fetchmany_impl(self, size=None):
  1040. return self._real_result._fetchmany_impl(size=size)
  1041. class ScalarResult(FilterResult):
  1042. """A wrapper for a :class:`_engine.Result` that returns scalar values
  1043. rather than :class:`_row.Row` values.
  1044. The :class:`_engine.ScalarResult` object is acquired by calling the
  1045. :meth:`_engine.Result.scalars` method.
  1046. A special limitation of :class:`_engine.ScalarResult` is that it has
  1047. no ``fetchone()`` method; since the semantics of ``fetchone()`` are that
  1048. the ``None`` value indicates no more results, this is not compatible
  1049. with :class:`_engine.ScalarResult` since there is no way to distinguish
  1050. between ``None`` as a row value versus ``None`` as an indicator. Use
  1051. ``next(result)`` to receive values individually.
  1052. """
  1053. _generate_rows = False
  1054. def __init__(self, real_result, index):
  1055. self._real_result = real_result
  1056. if real_result._source_supports_scalars:
  1057. self._metadata = real_result._metadata
  1058. self._post_creational_filter = None
  1059. else:
  1060. self._metadata = real_result._metadata._reduce([index])
  1061. self._post_creational_filter = operator.itemgetter(0)
  1062. self._unique_filter_state = real_result._unique_filter_state
  1063. def unique(self, strategy=None):
  1064. """Apply unique filtering to the objects returned by this
  1065. :class:`_engine.ScalarResult`.
  1066. See :meth:`_engine.Result.unique` for usage details.
  1067. """
  1068. self._unique_filter_state = (set(), strategy)
  1069. return self
  1070. def partitions(self, size=None):
  1071. """Iterate through sub-lists of elements of the size given.
  1072. Equivalent to :meth:`_engine.Result.partitions` except that
  1073. scalar values, rather than :class:`_engine.Row` objects,
  1074. are returned.
  1075. """
  1076. getter = self._manyrow_getter
  1077. while True:
  1078. partition = getter(self, size)
  1079. if partition:
  1080. yield partition
  1081. else:
  1082. break
  1083. def fetchall(self):
  1084. """A synonym for the :meth:`_engine.ScalarResult.all` method."""
  1085. return self._allrows()
  1086. def fetchmany(self, size=None):
  1087. """Fetch many objects.
  1088. Equivalent to :meth:`_engine.Result.fetchmany` except that
  1089. scalar values, rather than :class:`_engine.Row` objects,
  1090. are returned.
  1091. """
  1092. return self._manyrow_getter(self, size)
  1093. def all(self):
  1094. """Return all scalar values in a list.
  1095. Equivalent to :meth:`_engine.Result.all` except that
  1096. scalar values, rather than :class:`_engine.Row` objects,
  1097. are returned.
  1098. """
  1099. return self._allrows()
  1100. def __iter__(self):
  1101. return self._iter_impl()
  1102. def __next__(self):
  1103. return self._next_impl()
  1104. if py2k:
  1105. def next(self): # noqa
  1106. return self._next_impl()
  1107. def first(self):
  1108. """Fetch the first object or ``None`` if no object is present.
  1109. Equivalent to :meth:`_engine.Result.first` except that
  1110. scalar values, rather than :class:`_engine.Row` objects,
  1111. are returned.
  1112. """
  1113. return self._only_one_row(
  1114. raise_for_second_row=False, raise_for_none=False, scalar=False
  1115. )
  1116. def one_or_none(self):
  1117. """Return at most one object or raise an exception.
  1118. Equivalent to :meth:`_engine.Result.one_or_none` except that
  1119. scalar values, rather than :class:`_engine.Row` objects,
  1120. are returned.
  1121. """
  1122. return self._only_one_row(
  1123. raise_for_second_row=True, raise_for_none=False, scalar=False
  1124. )
  1125. def one(self):
  1126. """Return exactly one object or raise an exception.
  1127. Equivalent to :meth:`_engine.Result.one` except that
  1128. scalar values, rather than :class:`_engine.Row` objects,
  1129. are returned.
  1130. """
  1131. return self._only_one_row(
  1132. raise_for_second_row=True, raise_for_none=True, scalar=False
  1133. )
  1134. class MappingResult(_WithKeys, FilterResult):
  1135. """A wrapper for a :class:`_engine.Result` that returns dictionary values
  1136. rather than :class:`_engine.Row` values.
  1137. The :class:`_engine.MappingResult` object is acquired by calling the
  1138. :meth:`_engine.Result.mappings` method.
  1139. """
  1140. _generate_rows = True
  1141. _post_creational_filter = operator.attrgetter("_mapping")
  1142. def __init__(self, result):
  1143. self._real_result = result
  1144. self._unique_filter_state = result._unique_filter_state
  1145. self._metadata = result._metadata
  1146. if result._source_supports_scalars:
  1147. self._metadata = self._metadata._reduce([0])
  1148. def unique(self, strategy=None):
  1149. """Apply unique filtering to the objects returned by this
  1150. :class:`_engine.MappingResult`.
  1151. See :meth:`_engine.Result.unique` for usage details.
  1152. """
  1153. self._unique_filter_state = (set(), strategy)
  1154. return self
  1155. def columns(self, *col_expressions):
  1156. r"""Establish the columns that should be returned in each row."""
  1157. return self._column_slices(col_expressions)
  1158. def partitions(self, size=None):
  1159. """Iterate through sub-lists of elements of the size given.
  1160. Equivalent to :meth:`_engine.Result.partitions` except that
  1161. :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
  1162. objects, are returned.
  1163. """
  1164. getter = self._manyrow_getter
  1165. while True:
  1166. partition = getter(self, size)
  1167. if partition:
  1168. yield partition
  1169. else:
  1170. break
  1171. def fetchall(self):
  1172. """A synonym for the :meth:`_engine.MappingResult.all` method."""
  1173. return self._allrows()
  1174. def fetchone(self):
  1175. """Fetch one object.
  1176. Equivalent to :meth:`_engine.Result.fetchone` except that
  1177. :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
  1178. objects, are returned.
  1179. """
  1180. row = self._onerow_getter(self)
  1181. if row is _NO_ROW:
  1182. return None
  1183. else:
  1184. return row
  1185. def fetchmany(self, size=None):
  1186. """Fetch many objects.
  1187. Equivalent to :meth:`_engine.Result.fetchmany` except that
  1188. :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
  1189. objects, are returned.
  1190. """
  1191. return self._manyrow_getter(self, size)
  1192. def all(self):
  1193. """Return all scalar values in a list.
  1194. Equivalent to :meth:`_engine.Result.all` except that
  1195. :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
  1196. objects, are returned.
  1197. """
  1198. return self._allrows()
  1199. def __iter__(self):
  1200. return self._iter_impl()
  1201. def __next__(self):
  1202. return self._next_impl()
  1203. if py2k:
  1204. def next(self): # noqa
  1205. return self._next_impl()
  1206. def first(self):
  1207. """Fetch the first object or ``None`` if no object is present.
  1208. Equivalent to :meth:`_engine.Result.first` except that
  1209. :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
  1210. objects, are returned.
  1211. """
  1212. return self._only_one_row(
  1213. raise_for_second_row=False, raise_for_none=False, scalar=False
  1214. )
  1215. def one_or_none(self):
  1216. """Return at most one object or raise an exception.
  1217. Equivalent to :meth:`_engine.Result.one_or_none` except that
  1218. :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
  1219. objects, are returned.
  1220. """
  1221. return self._only_one_row(
  1222. raise_for_second_row=True, raise_for_none=False, scalar=False
  1223. )
  1224. def one(self):
  1225. """Return exactly one object or raise an exception.
  1226. Equivalent to :meth:`_engine.Result.one` except that
  1227. :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
  1228. objects, are returned.
  1229. """
  1230. return self._only_one_row(
  1231. raise_for_second_row=True, raise_for_none=True, scalar=False
  1232. )
  1233. class FrozenResult(object):
  1234. """Represents a :class:`_engine.Result` object in a "frozen" state suitable
  1235. for caching.
  1236. The :class:`_engine.FrozenResult` object is returned from the
  1237. :meth:`_engine.Result.freeze` method of any :class:`_engine.Result`
  1238. object.
  1239. A new iterable :class:`_engine.Result` object is generated from a fixed
  1240. set of data each time the :class:`_engine.FrozenResult` is invoked as
  1241. a callable::
  1242. result = connection.execute(query)
  1243. frozen = result.freeze()
  1244. unfrozen_result_one = frozen()
  1245. for row in unfrozen_result_one:
  1246. print(row)
  1247. unfrozen_result_two = frozen()
  1248. rows = unfrozen_result_two.all()
  1249. # ... etc
  1250. .. versionadded:: 1.4
  1251. .. seealso::
  1252. :ref:`do_orm_execute_re_executing` - example usage within the
  1253. ORM to implement a result-set cache.
  1254. :func:`_orm.loading.merge_frozen_result` - ORM function to merge
  1255. a frozen result back into a :class:`_orm.Session`.
  1256. """
  1257. def __init__(self, result):
  1258. self.metadata = result._metadata._for_freeze()
  1259. self._source_supports_scalars = result._source_supports_scalars
  1260. self._attributes = result._attributes
  1261. if self._source_supports_scalars:
  1262. self.data = list(result._raw_row_iterator())
  1263. else:
  1264. self.data = result.fetchall()
  1265. def rewrite_rows(self):
  1266. if self._source_supports_scalars:
  1267. return [[elem] for elem in self.data]
  1268. else:
  1269. return [list(row) for row in self.data]
  1270. def with_new_rows(self, tuple_data):
  1271. fr = FrozenResult.__new__(FrozenResult)
  1272. fr.metadata = self.metadata
  1273. fr._attributes = self._attributes
  1274. fr._source_supports_scalars = self._source_supports_scalars
  1275. if self._source_supports_scalars:
  1276. fr.data = [d[0] for d in tuple_data]
  1277. else:
  1278. fr.data = tuple_data
  1279. return fr
  1280. def __call__(self):
  1281. result = IteratorResult(self.metadata, iter(self.data))
  1282. result._attributes = self._attributes
  1283. result._source_supports_scalars = self._source_supports_scalars
  1284. return result
  1285. class IteratorResult(Result):
  1286. """A :class:`_engine.Result` that gets data from a Python iterator of
  1287. :class:`_engine.Row` objects or similar row-like data.
  1288. .. versionadded:: 1.4
  1289. """
  1290. _hard_closed = False
  1291. _soft_closed = False
  1292. def __init__(
  1293. self,
  1294. cursor_metadata,
  1295. iterator,
  1296. raw=None,
  1297. _source_supports_scalars=False,
  1298. ):
  1299. self._metadata = cursor_metadata
  1300. self.iterator = iterator
  1301. self.raw = raw
  1302. self._source_supports_scalars = _source_supports_scalars
  1303. @property
  1304. def closed(self):
  1305. """Return ``True`` if this :class:`_engine.IteratorResult` has
  1306. been closed
  1307. .. versionadded:: 1.4.43
  1308. """
  1309. return self._hard_closed
  1310. def _soft_close(self, hard=False, **kw):
  1311. if hard:
  1312. self._hard_closed = True
  1313. if self.raw is not None:
  1314. self.raw._soft_close(hard=hard, **kw)
  1315. self.iterator = iter([])
  1316. self._reset_memoizations()
  1317. self._soft_closed = True
  1318. def _raise_hard_closed(self):
  1319. raise exc.ResourceClosedError("This result object is closed.")
  1320. def _raw_row_iterator(self):
  1321. return self.iterator
  1322. def _fetchiter_impl(self):
  1323. if self._hard_closed:
  1324. self._raise_hard_closed()
  1325. return self.iterator
  1326. def _fetchone_impl(self, hard_close=False):
  1327. if self._hard_closed:
  1328. self._raise_hard_closed()
  1329. row = next(self.iterator, _NO_ROW)
  1330. if row is _NO_ROW:
  1331. self._soft_close(hard=hard_close)
  1332. return None
  1333. else:
  1334. return row
  1335. def _fetchall_impl(self):
  1336. if self._hard_closed:
  1337. self._raise_hard_closed()
  1338. try:
  1339. return list(self.iterator)
  1340. finally:
  1341. self._soft_close()
  1342. def _fetchmany_impl(self, size=None):
  1343. if self._hard_closed:
  1344. self._raise_hard_closed()
  1345. return list(itertools.islice(self.iterator, 0, size))
  1346. def null_result():
  1347. return IteratorResult(SimpleResultMetaData([]), iter([]))
  1348. class ChunkedIteratorResult(IteratorResult):
  1349. """An :class:`_engine.IteratorResult` that works from an
  1350. iterator-producing callable.
  1351. The given ``chunks`` argument is a function that is given a number of rows
  1352. to return in each chunk, or ``None`` for all rows. The function should
  1353. then return an un-consumed iterator of lists, each list of the requested
  1354. size.
  1355. The function can be called at any time again, in which case it should
  1356. continue from the same result set but adjust the chunk size as given.
  1357. .. versionadded:: 1.4
  1358. """
  1359. def __init__(
  1360. self,
  1361. cursor_metadata,
  1362. chunks,
  1363. source_supports_scalars=False,
  1364. raw=None,
  1365. dynamic_yield_per=False,
  1366. ):
  1367. self._metadata = cursor_metadata
  1368. self.chunks = chunks
  1369. self._source_supports_scalars = source_supports_scalars
  1370. self.raw = raw
  1371. self.iterator = itertools.chain.from_iterable(self.chunks(None))
  1372. self.dynamic_yield_per = dynamic_yield_per
  1373. @_generative
  1374. def yield_per(self, num):
  1375. # TODO: this throws away the iterator which may be holding
  1376. # onto a chunk. the yield_per cannot be changed once any
  1377. # rows have been fetched. either find a way to enforce this,
  1378. # or we can't use itertools.chain and will instead have to
  1379. # keep track.
  1380. self._yield_per = num
  1381. self.iterator = itertools.chain.from_iterable(self.chunks(num))
  1382. def _soft_close(self, **kw):
  1383. super(ChunkedIteratorResult, self)._soft_close(**kw)
  1384. self.chunks = lambda size: []
  1385. def _fetchmany_impl(self, size=None):
  1386. if self.dynamic_yield_per:
  1387. self.iterator = itertools.chain.from_iterable(self.chunks(size))
  1388. return super(ChunkedIteratorResult, self)._fetchmany_impl(size=size)
  1389. class MergedResult(IteratorResult):
  1390. """A :class:`_engine.Result` that is merged from any number of
  1391. :class:`_engine.Result` objects.
  1392. Returned by the :meth:`_engine.Result.merge` method.
  1393. .. versionadded:: 1.4
  1394. """
  1395. closed = False
  1396. def __init__(self, cursor_metadata, results):
  1397. self._results = results
  1398. super(MergedResult, self).__init__(
  1399. cursor_metadata,
  1400. itertools.chain.from_iterable(
  1401. r._raw_row_iterator() for r in results
  1402. ),
  1403. )
  1404. self._unique_filter_state = results[0]._unique_filter_state
  1405. self._yield_per = results[0]._yield_per
  1406. # going to try something w/ this in next rev
  1407. self._source_supports_scalars = results[0]._source_supports_scalars
  1408. self._attributes = self._attributes.merge_with(
  1409. *[r._attributes for r in results]
  1410. )
  1411. def _soft_close(self, hard=False, **kw):
  1412. for r in self._results:
  1413. r._soft_close(hard=hard, **kw)
  1414. if hard:
  1415. self.closed = True