Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/result.py: 45%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

570 statements  

1# engine/result.py 

2# Copyright (C) 2005-2025 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 

8"""Define generic result set constructs.""" 

9 

10from __future__ import annotations 

11 

12import functools 

13import itertools 

14import operator 

15import typing 

16from typing import Any 

17from typing import Callable 

18from typing import cast 

19from typing import Dict 

20from typing import Generic 

21from typing import Iterable 

22from typing import Iterator 

23from typing import List 

24from typing import Literal 

25from typing import Mapping 

26from typing import NoReturn 

27from typing import Optional 

28from typing import overload 

29from typing import Sequence 

30from typing import Tuple 

31from typing import TYPE_CHECKING 

32from typing import Union 

33 

34from ._result_cy import _InterimRowType 

35from ._result_cy import _NO_ROW as _NO_ROW 

36from ._result_cy import _R as _R 

37from ._result_cy import _RowData 

38from ._result_cy import _T 

39from ._result_cy import _UniqueFilterType as _UniqueFilterType 

40from ._result_cy import BaseResultInternal 

41from ._util_cy import tuplegetter as tuplegetter 

42from .row import Row 

43from .row import RowMapping 

44from .. import exc 

45from .. import util 

46from ..sql.base import _generative 

47from ..sql.base import InPlaceGenerative 

48from ..util import deprecated 

49from ..util import NONE_SET 

50from ..util.typing import Self 

51from ..util.typing import TupleAny 

52from ..util.typing import TypeVarTuple 

53from ..util.typing import Unpack 

54 

55if typing.TYPE_CHECKING: 

56 from ..sql.elements import SQLCoreOperations 

57 from ..sql.type_api import _ResultProcessorType 

58 

59_KeyType = Union[str, "SQLCoreOperations[Any]"] 

60_KeyIndexType = Union[_KeyType, int] 

61 

62# is overridden in cursor using _CursorKeyMapRecType 

63_KeyMapRecType = Any 

64 

65_KeyMapType = Mapping[_KeyType, _KeyMapRecType] 

66 

67 

68_Ts = TypeVarTuple("_Ts") 

69 

70 

71_InterimSupportsScalarsRowType = Union[Row[Unpack[TupleAny]], Any] 

72 

73_ProcessorsType = Sequence[Optional["_ResultProcessorType[Any]"]] 

74_TupleGetterType = Callable[[Sequence[Any]], Sequence[Any]] 

75 

76 

77class ResultMetaData: 

78 """Base for metadata about result rows.""" 

79 

80 __slots__ = () 

81 

82 _tuplefilter: Optional[_TupleGetterType] = None 

83 _translated_indexes: Optional[Sequence[int]] = None 

84 _unique_filters: Optional[Sequence[Callable[[Any], Any]]] = None 

85 _keymap: _KeyMapType 

86 _keys: Sequence[str] 

87 _processors: Optional[_ProcessorsType] 

88 _key_to_index: Dict[_KeyType, int] 

89 

90 @property 

91 def keys(self) -> RMKeyView: 

92 return RMKeyView(self) 

93 

94 def _has_key(self, key: object) -> bool: 

95 raise NotImplementedError() 

96 

97 def _for_freeze(self) -> ResultMetaData: 

98 raise NotImplementedError() 

99 

100 @overload 

101 def _key_fallback( 

102 self, key: Any, err: Optional[Exception], raiseerr: Literal[True] = ... 

103 ) -> NoReturn: ... 

104 

105 @overload 

106 def _key_fallback( 

107 self, 

108 key: Any, 

109 err: Optional[Exception], 

110 raiseerr: Literal[False] = ..., 

111 ) -> None: ... 

112 

113 @overload 

114 def _key_fallback( 

115 self, key: Any, err: Optional[Exception], raiseerr: bool = ... 

116 ) -> Optional[NoReturn]: ... 

117 

118 def _key_fallback( 

119 self, key: Any, err: Optional[Exception], raiseerr: bool = True 

120 ) -> Optional[NoReturn]: 

121 assert raiseerr 

122 raise KeyError(key) from err 

123 

124 def _raise_for_ambiguous_column_name( 

125 self, rec: _KeyMapRecType 

126 ) -> NoReturn: 

127 raise NotImplementedError( 

128 "ambiguous column name logic is implemented for " 

129 "CursorResultMetaData" 

130 ) 

131 

132 def _index_for_key( 

133 self, key: _KeyIndexType, raiseerr: bool 

134 ) -> Optional[int]: 

135 raise NotImplementedError() 

136 

137 def _indexes_for_keys( 

138 self, keys: Sequence[_KeyIndexType] 

139 ) -> Sequence[int]: 

140 raise NotImplementedError() 

141 

142 def _metadata_for_keys( 

143 self, keys: Sequence[_KeyIndexType] 

144 ) -> Iterator[_KeyMapRecType]: 

145 raise NotImplementedError() 

146 

147 def _reduce(self, keys: Sequence[_KeyIndexType]) -> ResultMetaData: 

148 raise NotImplementedError() 

149 

150 def _getter( 

151 self, key: Any, raiseerr: bool = True 

152 ) -> Optional[Callable[[Row[Unpack[TupleAny]]], Any]]: 

153 index = self._index_for_key(key, raiseerr) 

154 

155 if index is not None: 

156 return operator.itemgetter(index) 

157 else: 

158 return None 

159 

160 def _row_as_tuple_getter( 

161 self, keys: Sequence[_KeyIndexType] 

162 ) -> _TupleGetterType: 

163 indexes = self._indexes_for_keys(keys) 

164 return tuplegetter(*indexes) 

165 

166 def _make_key_to_index( 

167 self, keymap: Mapping[_KeyType, Sequence[Any]], index: int 

168 ) -> Dict[_KeyType, int]: 

169 return { 

170 key: rec[index] 

171 for key, rec in keymap.items() 

172 if rec[index] is not None 

173 } 

174 

175 def _key_not_found(self, key: Any, attr_error: bool) -> NoReturn: 

176 if key in self._keymap: 

177 # the index must be none in this case 

178 self._raise_for_ambiguous_column_name(self._keymap[key]) 

179 else: 

180 # unknown key 

181 if attr_error: 

182 try: 

183 self._key_fallback(key, None) 

184 except KeyError as ke: 

185 raise AttributeError(ke.args[0]) from ke 

186 else: 

187 self._key_fallback(key, None) 

188 

189 @property 

190 def _effective_processors(self) -> Optional[_ProcessorsType]: 

191 if not self._processors or NONE_SET.issuperset(self._processors): 

192 return None 

193 else: 

194 return self._processors 

195 

196 

197class RMKeyView(typing.KeysView[Any]): 

198 __slots__ = ("_parent", "_keys") 

199 

200 _parent: ResultMetaData 

201 _keys: Sequence[str] 

202 

203 def __init__(self, parent: ResultMetaData): 

204 self._parent = parent 

205 self._keys = [k for k in parent._keys if k is not None] 

206 

207 def __len__(self) -> int: 

208 return len(self._keys) 

209 

210 def __repr__(self) -> str: 

211 return "{0.__class__.__name__}({0._keys!r})".format(self) 

212 

213 def __iter__(self) -> Iterator[str]: 

214 return iter(self._keys) 

215 

216 def __contains__(self, item: Any) -> bool: 

217 if isinstance(item, int): 

218 return False 

219 

220 # note this also includes special key fallback behaviors 

221 # which also don't seem to be tested in test_resultset right now 

222 return self._parent._has_key(item) 

223 

224 def __eq__(self, other: Any) -> bool: 

225 return list(other) == list(self) 

226 

227 def __ne__(self, other: Any) -> bool: 

228 return list(other) != list(self) 

229 

230 

231class SimpleResultMetaData(ResultMetaData): 

232 """result metadata for in-memory collections.""" 

233 

234 __slots__ = ( 

235 "_keys", 

236 "_keymap", 

237 "_processors", 

238 "_tuplefilter", 

239 "_translated_indexes", 

240 "_unique_filters", 

241 "_key_to_index", 

242 ) 

243 

244 _keys: Sequence[str] 

245 

246 def __init__( 

247 self, 

248 keys: Sequence[str], 

249 extra: Optional[Sequence[Any]] = None, 

250 _processors: Optional[_ProcessorsType] = None, 

251 _tuplefilter: Optional[_TupleGetterType] = None, 

252 _translated_indexes: Optional[Sequence[int]] = None, 

253 _unique_filters: Optional[Sequence[Callable[[Any], Any]]] = None, 

254 ): 

255 self._keys = list(keys) 

256 self._tuplefilter = _tuplefilter 

257 self._translated_indexes = _translated_indexes 

258 self._unique_filters = _unique_filters 

259 if extra: 

260 assert len(self._keys) == len(extra) 

261 recs_names = [ 

262 ( 

263 (name,) + (extras if extras else ()), 

264 (index, name, extras), 

265 ) 

266 for index, (name, extras) in enumerate(zip(self._keys, extra)) 

267 ] 

268 else: 

269 recs_names = [ 

270 ((name,), (index, name, ())) 

271 for index, name in enumerate(self._keys) 

272 ] 

273 

274 self._keymap = {key: rec for keys, rec in recs_names for key in keys} 

275 

276 self._processors = _processors 

277 

278 self._key_to_index = self._make_key_to_index(self._keymap, 0) 

279 

280 def _has_key(self, key: object) -> bool: 

281 return key in self._keymap 

282 

283 def _for_freeze(self) -> ResultMetaData: 

284 unique_filters = self._unique_filters 

285 if unique_filters and self._tuplefilter: 

286 unique_filters = self._tuplefilter(unique_filters) 

287 

288 # TODO: are we freezing the result with or without uniqueness 

289 # applied? 

290 return SimpleResultMetaData( 

291 self._keys, 

292 extra=[self._keymap[key][2] for key in self._keys], 

293 _unique_filters=unique_filters, 

294 ) 

295 

296 def __getstate__(self) -> Dict[str, Any]: 

297 return { 

298 "_keys": self._keys, 

299 "_translated_indexes": self._translated_indexes, 

300 } 

301 

302 def __setstate__(self, state: Dict[str, Any]) -> None: 

303 if state["_translated_indexes"]: 

304 _translated_indexes = state["_translated_indexes"] 

305 _tuplefilter = tuplegetter(*_translated_indexes) 

306 else: 

307 _translated_indexes = _tuplefilter = None 

308 self.__init__( # type: ignore 

309 state["_keys"], 

310 _translated_indexes=_translated_indexes, 

311 _tuplefilter=_tuplefilter, 

312 ) 

313 

314 def _index_for_key(self, key: Any, raiseerr: bool = True) -> int: 

315 if isinstance(key, int): 

316 key = self._keys[key] 

317 try: 

318 rec = self._keymap[key] 

319 except KeyError as ke: 

320 rec = self._key_fallback(key, ke, raiseerr) 

321 

322 return rec[0] # type: ignore[no-any-return] 

323 

324 def _indexes_for_keys(self, keys: Sequence[Any]) -> Sequence[int]: 

325 return [self._keymap[key][0] for key in keys] 

326 

327 def _metadata_for_keys( 

328 self, keys: Sequence[Any] 

329 ) -> Iterator[_KeyMapRecType]: 

330 for key in keys: 

331 if isinstance(key, int): 

332 key = self._keys[key] 

333 

334 try: 

335 rec = self._keymap[key] 

336 except KeyError as ke: 

337 rec = self._key_fallback(key, ke, True) 

338 

339 yield rec 

340 

341 def _reduce(self, keys: Sequence[Any]) -> ResultMetaData: 

342 try: 

343 metadata_for_keys = [ 

344 self._keymap[self._keys[key] if isinstance(key, int) else key] 

345 for key in keys 

346 ] 

347 except KeyError as ke: 

348 self._key_fallback(ke.args[0], ke, True) 

349 

350 indexes: Sequence[int] 

351 new_keys: Sequence[str] 

352 extra: Sequence[Any] 

353 indexes, new_keys, extra = zip(*metadata_for_keys) 

354 

355 if self._translated_indexes: 

356 indexes = [self._translated_indexes[idx] for idx in indexes] 

357 

358 tup = tuplegetter(*indexes) 

359 

360 new_metadata = SimpleResultMetaData( 

361 new_keys, 

362 extra=extra, 

363 _tuplefilter=tup, 

364 _translated_indexes=indexes, 

365 _processors=self._processors, 

366 _unique_filters=self._unique_filters, 

367 ) 

368 

369 return new_metadata 

370 

371 

372def result_tuple( 

373 fields: Sequence[str], extra: Optional[Any] = None 

374) -> Callable[[Iterable[Any]], Row[Unpack[TupleAny]]]: 

375 parent = SimpleResultMetaData(fields, extra) 

376 return functools.partial( 

377 Row, parent, parent._effective_processors, parent._key_to_index 

378 ) 

379 

380 

381class ResultInternal(InPlaceGenerative, BaseResultInternal[_R]): 

382 __slots__ = () 

383 _is_cursor = False 

384 

385 @_generative 

386 def _column_slices(self, indexes: Sequence[_KeyIndexType]) -> Self: 

387 real_result = ( 

388 self._real_result 

389 if self._real_result 

390 else cast("Result[Any]", self) 

391 ) 

392 

393 if not real_result._source_supports_scalars or len(indexes) != 1: 

394 self._metadata = self._metadata._reduce(indexes) 

395 

396 assert self._generate_rows 

397 

398 return self 

399 

400 

401class _WithKeys: 

402 __slots__ = () 

403 

404 _metadata: ResultMetaData 

405 

406 # used mainly to share documentation on the keys method. 

407 def keys(self) -> RMKeyView: 

408 """Return an iterable view which yields the string keys that would 

409 be represented by each :class:`_engine.Row`. 

410 

411 The keys can represent the labels of the columns returned by a core 

412 statement or the names of the orm classes returned by an orm 

413 execution. 

414 

415 The view also can be tested for key containment using the Python 

416 ``in`` operator, which will test both for the string keys represented 

417 in the view, as well as for alternate keys such as column objects. 

418 

419 .. versionchanged:: 1.4 a key view object is returned rather than a 

420 plain list. 

421 

422 

423 """ 

424 return self._metadata.keys 

425 

426 

427class Result(_WithKeys, ResultInternal[Row[Unpack[_Ts]]]): 

428 """Represent a set of database results. 

429 

430 .. versionadded:: 1.4 The :class:`_engine.Result` object provides a 

431 completely updated usage model and calling facade for SQLAlchemy 

432 Core and SQLAlchemy ORM. In Core, it forms the basis of the 

433 :class:`_engine.CursorResult` object which replaces the previous 

434 :class:`_engine.ResultProxy` interface. When using the ORM, a 

435 higher level object called :class:`_engine.ChunkedIteratorResult` 

436 is normally used. 

437 

438 .. note:: In SQLAlchemy 1.4 and above, this object is 

439 used for ORM results returned by :meth:`_orm.Session.execute`, which can 

440 yield instances of ORM mapped objects either individually or within 

441 tuple-like rows. Note that the :class:`_engine.Result` object does not 

442 deduplicate instances or rows automatically as is the case with the 

443 legacy :class:`_orm.Query` object. For in-Python de-duplication of 

444 instances or rows, use the :meth:`_engine.Result.unique` modifier 

445 method. 

446 

447 .. seealso:: 

448 

449 :ref:`tutorial_fetching_rows` - in the :doc:`/tutorial/index` 

450 

451 """ 

452 

453 __slots__ = ("_metadata", "__dict__") 

454 

455 _row_logging_fn: Optional[ 

456 Callable[[Row[Unpack[TupleAny]]], Row[Unpack[TupleAny]]] 

457 ] = None 

458 

459 _source_supports_scalars: bool = False 

460 

461 _yield_per: Optional[int] = None 

462 

463 _attributes: util.immutabledict[Any, Any] = util.immutabledict() 

464 

465 def __init__(self, cursor_metadata: ResultMetaData): 

466 self._metadata = cursor_metadata 

467 

468 def __enter__(self) -> Self: 

469 return self 

470 

471 def __exit__(self, type_: Any, value: Any, traceback: Any) -> None: 

472 self.close() 

473 

474 def close(self) -> None: 

475 """close this :class:`_engine.Result`. 

476 

477 The behavior of this method is implementation specific, and is 

478 not implemented by default. The method should generally end 

479 the resources in use by the result object and also cause any 

480 subsequent iteration or row fetching to raise 

481 :class:`.ResourceClosedError`. 

482 

483 .. versionadded:: 1.4.27 - ``.close()`` was previously not generally 

484 available for all :class:`_engine.Result` classes, instead only 

485 being available on the :class:`_engine.CursorResult` returned for 

486 Core statement executions. As most other result objects, namely the 

487 ones used by the ORM, are proxying a :class:`_engine.CursorResult` 

488 in any case, this allows the underlying cursor result to be closed 

489 from the outside facade for the case when the ORM query is using 

490 the ``yield_per`` execution option where it does not immediately 

491 exhaust and autoclose the database cursor. 

492 

493 """ 

494 self._soft_close(hard=True) 

495 

496 @property 

497 def _soft_closed(self) -> bool: 

498 raise NotImplementedError() 

499 

500 @property 

501 def closed(self) -> bool: 

502 """return ``True`` if this :class:`_engine.Result` reports .closed 

503 

504 .. versionadded:: 1.4.43 

505 

506 """ 

507 raise NotImplementedError() 

508 

509 @_generative 

510 def yield_per(self, num: int) -> Self: 

511 """Configure the row-fetching strategy to fetch ``num`` rows at a time. 

512 

513 This impacts the underlying behavior of the result when iterating over 

514 the result object, or otherwise making use of methods such as 

515 :meth:`_engine.Result.fetchone` that return one row at a time. Data 

516 from the underlying cursor or other data source will be buffered up to 

517 this many rows in memory, and the buffered collection will then be 

518 yielded out one row at a time or as many rows are requested. Each time 

519 the buffer clears, it will be refreshed to this many rows or as many 

520 rows remain if fewer remain. 

521 

522 The :meth:`_engine.Result.yield_per` method is generally used in 

523 conjunction with the 

524 :paramref:`_engine.Connection.execution_options.stream_results` 

525 execution option, which will allow the database dialect in use to make 

526 use of a server side cursor, if the DBAPI supports a specific "server 

527 side cursor" mode separate from its default mode of operation. 

528 

529 .. tip:: 

530 

531 Consider using the 

532 :paramref:`_engine.Connection.execution_options.yield_per` 

533 execution option, which will simultaneously set 

534 :paramref:`_engine.Connection.execution_options.stream_results` 

535 to ensure the use of server side cursors, as well as automatically 

536 invoke the :meth:`_engine.Result.yield_per` method to establish 

537 a fixed row buffer size at once. 

538 

539 The :paramref:`_engine.Connection.execution_options.yield_per` 

540 execution option is available for ORM operations, with 

541 :class:`_orm.Session`-oriented use described at 

542 :ref:`orm_queryguide_yield_per`. The Core-only version which works 

543 with :class:`_engine.Connection` is new as of SQLAlchemy 1.4.40. 

544 

545 .. versionadded:: 1.4 

546 

547 :param num: number of rows to fetch each time the buffer is refilled. 

548 If set to a value below 1, fetches all rows for the next buffer. 

549 

550 .. seealso:: 

551 

552 :ref:`engine_stream_results` - describes Core behavior for 

553 :meth:`_engine.Result.yield_per` 

554 

555 :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel` 

556 

557 """ 

558 self._yield_per = num 

559 return self 

560 

561 @_generative 

562 def unique(self, strategy: Optional[_UniqueFilterType] = None) -> Self: 

563 """Apply unique filtering to the objects returned by this 

564 :class:`_engine.Result`. 

565 

566 When this filter is applied with no arguments, the rows or objects 

567 returned will filtered such that each row is returned uniquely. The 

568 algorithm used to determine this uniqueness is by default the Python 

569 hashing identity of the whole tuple. In some cases a specialized 

570 per-entity hashing scheme may be used, such as when using the ORM, a 

571 scheme is applied which works against the primary key identity of 

572 returned objects. 

573 

574 The unique filter is applied **after all other filters**, which means 

575 if the columns returned have been refined using a method such as the 

576 :meth:`_engine.Result.columns` or :meth:`_engine.Result.scalars` 

577 method, the uniquing is applied to **only the column or columns 

578 returned**. This occurs regardless of the order in which these 

579 methods have been called upon the :class:`_engine.Result` object. 

580 

581 The unique filter also changes the calculus used for methods like 

582 :meth:`_engine.Result.fetchmany` and :meth:`_engine.Result.partitions`. 

583 When using :meth:`_engine.Result.unique`, these methods will continue 

584 to yield the number of rows or objects requested, after uniquing 

585 has been applied. However, this necessarily impacts the buffering 

586 behavior of the underlying cursor or datasource, such that multiple 

587 underlying calls to ``cursor.fetchmany()`` may be necessary in order 

588 to accumulate enough objects in order to provide a unique collection 

589 of the requested size. 

590 

591 :param strategy: a callable that will be applied to rows or objects 

592 being iterated, which should return an object that represents the 

593 unique value of the row. A Python ``set()`` is used to store 

594 these identities. If not passed, a default uniqueness strategy 

595 is used which may have been assembled by the source of this 

596 :class:`_engine.Result` object. 

597 

598 """ 

599 self._unique_filter_state = (set(), strategy) 

600 return self 

601 

602 def columns(self, *col_expressions: _KeyIndexType) -> Self: 

603 r"""Establish the columns that should be returned in each row. 

604 

605 This method may be used to limit the columns returned as well 

606 as to reorder them. The given list of expressions are normally 

607 a series of integers or string key names. They may also be 

608 appropriate :class:`.ColumnElement` objects which correspond to 

609 a given statement construct. 

610 

611 .. versionchanged:: 2.0 Due to a bug in 1.4, the 

612 :meth:`_engine.Result.columns` method had an incorrect behavior 

613 where calling upon the method with just one index would cause the 

614 :class:`_engine.Result` object to yield scalar values rather than 

615 :class:`_engine.Row` objects. In version 2.0, this behavior 

616 has been corrected such that calling upon 

617 :meth:`_engine.Result.columns` with a single index will 

618 produce a :class:`_engine.Result` object that continues 

619 to yield :class:`_engine.Row` objects, which include 

620 only a single column. 

621 

622 E.g.:: 

623 

624 statement = select(table.c.x, table.c.y, table.c.z) 

625 result = connection.execute(statement) 

626 

627 for z, y in result.columns("z", "y"): 

628 ... 

629 

630 Example of using the column objects from the statement itself:: 

631 

632 for z, y in result.columns( 

633 statement.selected_columns.c.z, statement.selected_columns.c.y 

634 ): 

635 ... 

636 

637 .. versionadded:: 1.4 

638 

639 :param \*col_expressions: indicates columns to be returned. Elements 

640 may be integer row indexes, string column names, or appropriate 

641 :class:`.ColumnElement` objects corresponding to a select construct. 

642 

643 :return: this :class:`_engine.Result` object with the modifications 

644 given. 

645 

646 """ 

647 return self._column_slices(col_expressions) 

648 

649 @overload 

650 def scalars(self: Result[_T, Unpack[TupleAny]]) -> ScalarResult[_T]: ... 

651 

652 @overload 

653 def scalars( 

654 self: Result[_T, Unpack[TupleAny]], index: Literal[0] 

655 ) -> ScalarResult[_T]: ... 

656 

657 @overload 

658 def scalars(self, index: _KeyIndexType = 0) -> ScalarResult[Any]: ... 

659 

660 def scalars(self, index: _KeyIndexType = 0) -> ScalarResult[Any]: 

661 """Return a :class:`_engine.ScalarResult` filtering object which 

662 will return single elements rather than :class:`_row.Row` objects. 

663 

664 E.g.:: 

665 

666 >>> result = conn.execute(text("select int_id from table")) 

667 >>> result.scalars().all() 

668 [1, 2, 3] 

669 

670 When results are fetched from the :class:`_engine.ScalarResult` 

671 filtering object, the single column-row that would be returned by the 

672 :class:`_engine.Result` is instead returned as the column's value. 

673 

674 .. versionadded:: 1.4 

675 

676 :param index: integer or row key indicating the column to be fetched 

677 from each row, defaults to ``0`` indicating the first column. 

678 

679 :return: a new :class:`_engine.ScalarResult` filtering object referring 

680 to this :class:`_engine.Result` object. 

681 

682 """ 

683 return ScalarResult(self, index) 

684 

685 def _getter( 

686 self, key: _KeyIndexType, raiseerr: bool = True 

687 ) -> Optional[Callable[[Row[Unpack[TupleAny]]], Any]]: 

688 """return a callable that will retrieve the given key from a 

689 :class:`_engine.Row`. 

690 

691 """ 

692 if self._source_supports_scalars: 

693 raise NotImplementedError( 

694 "can't use this function in 'only scalars' mode" 

695 ) 

696 return self._metadata._getter(key, raiseerr) 

697 

698 def _tuple_getter(self, keys: Sequence[_KeyIndexType]) -> _TupleGetterType: 

699 """return a callable that will retrieve the given keys from a 

700 :class:`_engine.Row`. 

701 

702 """ 

703 if self._source_supports_scalars: 

704 raise NotImplementedError( 

705 "can't use this function in 'only scalars' mode" 

706 ) 

707 return self._metadata._row_as_tuple_getter(keys) 

708 

709 def mappings(self) -> MappingResult: 

710 """Apply a mappings filter to returned rows, returning an instance of 

711 :class:`_engine.MappingResult`. 

712 

713 When this filter is applied, fetching rows will return 

714 :class:`_engine.RowMapping` objects instead of :class:`_engine.Row` 

715 objects. 

716 

717 .. versionadded:: 1.4 

718 

719 :return: a new :class:`_engine.MappingResult` filtering object 

720 referring to this :class:`_engine.Result` object. 

721 

722 """ 

723 

724 return MappingResult(self) 

725 

726 @property 

727 @deprecated( 

728 "2.1.0", 

729 "The :attr:`.Result.t` method is deprecated, :class:`.Row` " 

730 "now behaves like a tuple and can unpack types directly.", 

731 ) 

732 def t(self) -> TupleResult[Tuple[Unpack[_Ts]]]: 

733 """Apply a "typed tuple" typing filter to returned rows. 

734 

735 The :attr:`_engine.Result.t` attribute is a synonym for 

736 calling the :meth:`_engine.Result.tuples` method. 

737 

738 .. versionadded:: 2.0 

739 

740 .. seealso:: 

741 

742 :ref:`change_10635` - describes a migration path from this 

743 workaround for SQLAlchemy 2.1. 

744 

745 """ 

746 return self # type: ignore 

747 

748 @deprecated( 

749 "2.1.0", 

750 "The :meth:`.Result.tuples` method is deprecated, :class:`.Row` " 

751 "now behaves like a tuple and can unpack types directly.", 

752 ) 

753 def tuples(self) -> TupleResult[Tuple[Unpack[_Ts]]]: 

754 """Apply a "typed tuple" typing filter to returned rows. 

755 

756 This method returns the same :class:`_engine.Result` object 

757 at runtime, 

758 however annotates as returning a :class:`_engine.TupleResult` object 

759 that will indicate to :pep:`484` typing tools that plain typed 

760 ``Tuple`` instances are returned rather than rows. This allows 

761 tuple unpacking and ``__getitem__`` access of :class:`_engine.Row` 

762 objects to by typed, for those cases where the statement invoked 

763 itself included typing information. 

764 

765 .. versionadded:: 2.0 

766 

767 :return: the :class:`_engine.TupleResult` type at typing time. 

768 

769 .. seealso:: 

770 

771 :ref:`change_10635` - describes a migration path from this 

772 workaround for SQLAlchemy 2.1. 

773 

774 :attr:`_engine.Result.t` - shorter synonym 

775 

776 :attr:`_engine.Row._t` - :class:`_engine.Row` version 

777 

778 """ 

779 

780 return self # type: ignore 

781 

782 def _raw_row_iterator(self) -> Iterator[_RowData]: 

783 """Return a safe iterator that yields raw row data. 

784 

785 This is used by the :meth:`_engine.Result.merge` method 

786 to merge multiple compatible results together. 

787 

788 """ 

789 raise NotImplementedError() 

790 

791 def __iter__(self) -> Iterator[Row[Unpack[_Ts]]]: 

792 return self._iter_impl() 

793 

794 def __next__(self) -> Row[Unpack[_Ts]]: 

795 return self._next_impl() 

796 

797 def partitions( 

798 self, size: Optional[int] = None 

799 ) -> Iterator[Sequence[Row[Unpack[_Ts]]]]: 

800 """Iterate through sub-lists of rows of the size given. 

801 

802 Each list will be of the size given, excluding the last list to 

803 be yielded, which may have a small number of rows. No empty 

804 lists will be yielded. 

805 

806 The result object is automatically closed when the iterator 

807 is fully consumed. 

808 

809 Note that the backend driver will usually buffer the entire result 

810 ahead of time unless the 

811 :paramref:`.Connection.execution_options.stream_results` execution 

812 option is used indicating that the driver should not pre-buffer 

813 results, if possible. Not all drivers support this option and 

814 the option is silently ignored for those who do not. 

815 

816 When using the ORM, the :meth:`_engine.Result.partitions` method 

817 is typically more effective from a memory perspective when it is 

818 combined with use of the 

819 :ref:`yield_per execution option <orm_queryguide_yield_per>`, 

820 which instructs both the DBAPI driver to use server side cursors, 

821 if available, as well as instructs the ORM loading internals to only 

822 build a certain amount of ORM objects from a result at a time before 

823 yielding them out. 

824 

825 .. versionadded:: 1.4 

826 

827 :param size: indicate the maximum number of rows to be present 

828 in each list yielded. If None, makes use of the value set by 

829 the :meth:`_engine.Result.yield_per`, method, if it were called, 

830 or the :paramref:`_engine.Connection.execution_options.yield_per` 

831 execution option, which is equivalent in this regard. If 

832 yield_per weren't set, it makes use of the 

833 :meth:`_engine.Result.fetchmany` default, which may be backend 

834 specific and not well defined. 

835 

836 :return: iterator of lists 

837 

838 .. seealso:: 

839 

840 :ref:`engine_stream_results` 

841 

842 :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel` 

843 

844 """ 

845 

846 getter = self._manyrow_getter 

847 

848 while True: 

849 partition = getter(self, size) 

850 if partition: 

851 yield partition 

852 else: 

853 break 

854 

855 def fetchall(self) -> Sequence[Row[Unpack[_Ts]]]: 

856 """A synonym for the :meth:`_engine.Result.all` method.""" 

857 

858 return self._allrows() 

859 

860 def fetchone(self) -> Optional[Row[Unpack[_Ts]]]: 

861 """Fetch one row. 

862 

863 When all rows are exhausted, returns None. 

864 

865 This method is provided for backwards compatibility with 

866 SQLAlchemy 1.x.x. 

867 

868 To fetch the first row of a result only, use the 

869 :meth:`_engine.Result.first` method. To iterate through all 

870 rows, iterate the :class:`_engine.Result` object directly. 

871 

872 :return: a :class:`_engine.Row` object if no filters are applied, 

873 or ``None`` if no rows remain. 

874 

875 """ 

876 row = self._onerow_getter(self) 

877 if row is _NO_ROW: 

878 return None 

879 else: 

880 return row 

881 

882 def fetchmany( 

883 self, size: Optional[int] = None 

884 ) -> Sequence[Row[Unpack[_Ts]]]: 

885 """Fetch many rows. 

886 

887 When all rows are exhausted, returns an empty sequence. 

888 

889 This method is provided for backwards compatibility with 

890 SQLAlchemy 1.x.x. 

891 

892 To fetch rows in groups, use the :meth:`_engine.Result.partitions` 

893 method. 

894 

895 :return: a sequence of :class:`_engine.Row` objects. 

896 

897 .. seealso:: 

898 

899 :meth:`_engine.Result.partitions` 

900 

901 """ 

902 

903 return self._manyrow_getter(self, size) 

904 

905 def all(self) -> Sequence[Row[Unpack[_Ts]]]: 

906 """Return all rows in a sequence. 

907 

908 Closes the result set after invocation. Subsequent invocations 

909 will return an empty sequence. 

910 

911 .. versionadded:: 1.4 

912 

913 :return: a sequence of :class:`_engine.Row` objects. 

914 

915 .. seealso:: 

916 

917 :ref:`engine_stream_results` - How to stream a large result set 

918 without loading it completely in python. 

919 

920 """ 

921 

922 return self._allrows() 

923 

924 def first(self) -> Optional[Row[Unpack[_Ts]]]: 

925 """Fetch the first row or ``None`` if no row is present. 

926 

927 Closes the result set and discards remaining rows. 

928 

929 .. note:: This method returns one **row**, e.g. tuple, by default. 

930 To return exactly one single scalar value, that is, the first 

931 column of the first row, use the 

932 :meth:`_engine.Result.scalar` method, 

933 or combine :meth:`_engine.Result.scalars` and 

934 :meth:`_engine.Result.first`. 

935 

936 Additionally, in contrast to the behavior of the legacy ORM 

937 :meth:`_orm.Query.first` method, **no limit is applied** to the 

938 SQL query which was invoked to produce this 

939 :class:`_engine.Result`; 

940 for a DBAPI driver that buffers results in memory before yielding 

941 rows, all rows will be sent to the Python process and all but 

942 the first row will be discarded. 

943 

944 .. seealso:: 

945 

946 :ref:`migration_20_unify_select` 

947 

948 :return: a :class:`_engine.Row` object, or None 

949 if no rows remain. 

950 

951 .. seealso:: 

952 

953 :meth:`_engine.Result.scalar` 

954 

955 :meth:`_engine.Result.one` 

956 

957 """ 

958 

959 return self._only_one_row( 

960 raise_for_second_row=False, raise_for_none=False, scalar=False 

961 ) 

962 

963 def one_or_none(self) -> Optional[Row[Unpack[_Ts]]]: 

964 """Return at most one result or raise an exception. 

965 

966 Returns ``None`` if the result has no rows. 

967 Raises :class:`.MultipleResultsFound` 

968 if multiple rows are returned. 

969 

970 .. versionadded:: 1.4 

971 

972 :return: The first :class:`_engine.Row` or ``None`` if no row 

973 is available. 

974 

975 :raises: :class:`.MultipleResultsFound` 

976 

977 .. seealso:: 

978 

979 :meth:`_engine.Result.first` 

980 

981 :meth:`_engine.Result.one` 

982 

983 """ 

984 return self._only_one_row( 

985 raise_for_second_row=True, raise_for_none=False, scalar=False 

986 ) 

987 

988 def scalar_one(self: Result[_T, Unpack[TupleAny]]) -> _T: 

989 """Return exactly one scalar result or raise an exception. 

990 

991 This is equivalent to calling :meth:`_engine.Result.scalars` and 

992 then :meth:`_engine.ScalarResult.one`. 

993 

994 .. seealso:: 

995 

996 :meth:`_engine.ScalarResult.one` 

997 

998 :meth:`_engine.Result.scalars` 

999 

1000 """ 

1001 return self._only_one_row( 

1002 raise_for_second_row=True, raise_for_none=True, scalar=True 

1003 ) 

1004 

1005 def scalar_one_or_none(self: Result[_T, Unpack[TupleAny]]) -> Optional[_T]: 

1006 """Return exactly one scalar result or ``None``. 

1007 

1008 This is equivalent to calling :meth:`_engine.Result.scalars` and 

1009 then :meth:`_engine.ScalarResult.one_or_none`. 

1010 

1011 .. seealso:: 

1012 

1013 :meth:`_engine.ScalarResult.one_or_none` 

1014 

1015 :meth:`_engine.Result.scalars` 

1016 

1017 """ 

1018 return self._only_one_row( 

1019 raise_for_second_row=True, raise_for_none=False, scalar=True 

1020 ) 

1021 

1022 def one(self) -> Row[Unpack[_Ts]]: 

1023 """Return exactly one row or raise an exception. 

1024 

1025 Raises :class:`_exc.NoResultFound` if the result returns no 

1026 rows, or :class:`_exc.MultipleResultsFound` if multiple rows 

1027 would be returned. 

1028 

1029 .. note:: This method returns one **row**, e.g. tuple, by default. 

1030 To return exactly one single scalar value, that is, the first 

1031 column of the first row, use the 

1032 :meth:`_engine.Result.scalar_one` method, or combine 

1033 :meth:`_engine.Result.scalars` and 

1034 :meth:`_engine.Result.one`. 

1035 

1036 .. versionadded:: 1.4 

1037 

1038 :return: The first :class:`_engine.Row`. 

1039 

1040 :raises: :class:`.MultipleResultsFound`, :class:`.NoResultFound` 

1041 

1042 .. seealso:: 

1043 

1044 :meth:`_engine.Result.first` 

1045 

1046 :meth:`_engine.Result.one_or_none` 

1047 

1048 :meth:`_engine.Result.scalar_one` 

1049 

1050 """ 

1051 return self._only_one_row( 

1052 raise_for_second_row=True, raise_for_none=True, scalar=False 

1053 ) 

1054 

1055 def scalar(self: Result[_T, Unpack[TupleAny]]) -> Optional[_T]: 

1056 """Fetch the first column of the first row, and close the result set. 

1057 

1058 Returns ``None`` if there are no rows to fetch. 

1059 

1060 No validation is performed to test if additional rows remain. 

1061 

1062 After calling this method, the object is fully closed, 

1063 e.g. the :meth:`_engine.CursorResult.close` 

1064 method will have been called. 

1065 

1066 :return: a Python scalar value, or ``None`` if no rows remain. 

1067 

1068 """ 

1069 return self._only_one_row( 

1070 raise_for_second_row=False, raise_for_none=False, scalar=True 

1071 ) 

1072 

1073 def freeze(self) -> FrozenResult[Unpack[_Ts]]: 

1074 """Return a callable object that will produce copies of this 

1075 :class:`_engine.Result` when invoked. 

1076 

1077 The callable object returned is an instance of 

1078 :class:`_engine.FrozenResult`. 

1079 

1080 This is used for result set caching. The method must be called 

1081 on the result when it has been unconsumed, and calling the method 

1082 will consume the result fully. When the :class:`_engine.FrozenResult` 

1083 is retrieved from a cache, it can be called any number of times where 

1084 it will produce a new :class:`_engine.Result` object each time 

1085 against its stored set of rows. 

1086 

1087 .. seealso:: 

1088 

1089 :ref:`do_orm_execute_re_executing` - example usage within the 

1090 ORM to implement a result-set cache. 

1091 

1092 """ 

1093 

1094 return FrozenResult(self) 

1095 

1096 def merge( 

1097 self, *others: Result[Unpack[TupleAny]] 

1098 ) -> MergedResult[Unpack[TupleAny]]: 

1099 """Merge this :class:`_engine.Result` with other compatible result 

1100 objects. 

1101 

1102 The object returned is an instance of :class:`_engine.MergedResult`, 

1103 which will be composed of iterators from the given result 

1104 objects. 

1105 

1106 The new result will use the metadata from this result object. 

1107 The subsequent result objects must be against an identical 

1108 set of result / cursor metadata, otherwise the behavior is 

1109 undefined. 

1110 

1111 """ 

1112 return MergedResult(self._metadata, (self,) + others) 

1113 

1114 

1115class FilterResult(ResultInternal[_R]): 

1116 """A wrapper for a :class:`_engine.Result` that returns objects other than 

1117 :class:`_engine.Row` objects, such as dictionaries or scalar objects. 

1118 

1119 :class:`_engine.FilterResult` is the common base for additional result 

1120 APIs including :class:`_engine.MappingResult`, 

1121 :class:`_engine.ScalarResult` and :class:`_engine.AsyncResult`. 

1122 

1123 """ 

1124 

1125 __slots__ = ( 

1126 "_real_result", 

1127 "_post_creational_filter", 

1128 "_metadata", 

1129 "_unique_filter_state", 

1130 "__dict__", 

1131 ) 

1132 

1133 _post_creational_filter: Optional[Callable[[Any], Any]] 

1134 

1135 _real_result: Result[Unpack[TupleAny]] 

1136 

1137 def __enter__(self) -> Self: 

1138 return self 

1139 

1140 def __exit__(self, type_: Any, value: Any, traceback: Any) -> None: 

1141 self._real_result.__exit__(type_, value, traceback) 

1142 

1143 @_generative 

1144 def yield_per(self, num: int) -> Self: 

1145 """Configure the row-fetching strategy to fetch ``num`` rows at a time. 

1146 

1147 The :meth:`_engine.FilterResult.yield_per` method is a pass through 

1148 to the :meth:`_engine.Result.yield_per` method. See that method's 

1149 documentation for usage notes. 

1150 

1151 .. versionadded:: 1.4.40 - added :meth:`_engine.FilterResult.yield_per` 

1152 so that the method is available on all result set implementations 

1153 

1154 .. seealso:: 

1155 

1156 :ref:`engine_stream_results` - describes Core behavior for 

1157 :meth:`_engine.Result.yield_per` 

1158 

1159 :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel` 

1160 

1161 """ 

1162 self._real_result = self._real_result.yield_per(num) 

1163 return self 

1164 

1165 def _soft_close(self, hard: bool = False) -> None: 

1166 self._real_result._soft_close(hard=hard) 

1167 

1168 @property 

1169 def _soft_closed(self) -> bool: 

1170 return self._real_result._soft_closed 

1171 

1172 @property 

1173 def closed(self) -> bool: 

1174 """Return ``True`` if the underlying :class:`_engine.Result` reports 

1175 closed 

1176 

1177 .. versionadded:: 1.4.43 

1178 

1179 """ 

1180 return self._real_result.closed 

1181 

1182 def close(self) -> None: 

1183 """Close this :class:`_engine.FilterResult`. 

1184 

1185 .. versionadded:: 1.4.43 

1186 

1187 """ 

1188 self._real_result.close() 

1189 

1190 @property 

1191 def _attributes(self) -> Dict[Any, Any]: 

1192 return self._real_result._attributes 

1193 

1194 def _fetchiter_impl( 

1195 self, 

1196 ) -> Iterator[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1197 return self._real_result._fetchiter_impl() 

1198 

1199 def _fetchone_impl( 

1200 self, hard_close: bool = False 

1201 ) -> Optional[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1202 return self._real_result._fetchone_impl(hard_close=hard_close) 

1203 

1204 def _fetchall_impl( 

1205 self, 

1206 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1207 return self._real_result._fetchall_impl() 

1208 

1209 def _fetchmany_impl( 

1210 self, size: Optional[int] = None 

1211 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1212 return self._real_result._fetchmany_impl(size=size) 

1213 

1214 

1215class ScalarResult(FilterResult[_R]): 

1216 """A wrapper for a :class:`_engine.Result` that returns scalar values 

1217 rather than :class:`_row.Row` values. 

1218 

1219 The :class:`_engine.ScalarResult` object is acquired by calling the 

1220 :meth:`_engine.Result.scalars` method. 

1221 

1222 A special limitation of :class:`_engine.ScalarResult` is that it has 

1223 no ``fetchone()`` method; since the semantics of ``fetchone()`` are that 

1224 the ``None`` value indicates no more results, this is not compatible 

1225 with :class:`_engine.ScalarResult` since there is no way to distinguish 

1226 between ``None`` as a row value versus ``None`` as an indicator. Use 

1227 ``next(result)`` to receive values individually. 

1228 

1229 """ 

1230 

1231 __slots__ = () 

1232 

1233 _generate_rows = False 

1234 

1235 _post_creational_filter: Optional[Callable[[Any], Any]] 

1236 

1237 def __init__( 

1238 self, real_result: Result[Unpack[TupleAny]], index: _KeyIndexType 

1239 ): 

1240 self._real_result = real_result 

1241 

1242 if real_result._source_supports_scalars: 

1243 self._metadata = real_result._metadata 

1244 self._post_creational_filter = None 

1245 else: 

1246 self._metadata = real_result._metadata._reduce([index]) 

1247 self._post_creational_filter = operator.itemgetter(0) 

1248 

1249 self._unique_filter_state = real_result._unique_filter_state 

1250 

1251 def unique(self, strategy: Optional[_UniqueFilterType] = None) -> Self: 

1252 """Apply unique filtering to the objects returned by this 

1253 :class:`_engine.ScalarResult`. 

1254 

1255 See :meth:`_engine.Result.unique` for usage details. 

1256 

1257 """ 

1258 self._unique_filter_state = (set(), strategy) 

1259 return self 

1260 

1261 def partitions(self, size: Optional[int] = None) -> Iterator[Sequence[_R]]: 

1262 """Iterate through sub-lists of elements of the size given. 

1263 

1264 Equivalent to :meth:`_engine.Result.partitions` except that 

1265 scalar values, rather than :class:`_engine.Row` objects, 

1266 are returned. 

1267 

1268 """ 

1269 

1270 getter = self._manyrow_getter 

1271 

1272 while True: 

1273 partition = getter(self, size) 

1274 if partition: 

1275 yield partition 

1276 else: 

1277 break 

1278 

1279 def fetchall(self) -> Sequence[_R]: 

1280 """A synonym for the :meth:`_engine.ScalarResult.all` method.""" 

1281 

1282 return self._allrows() 

1283 

1284 def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]: 

1285 """Fetch many objects. 

1286 

1287 Equivalent to :meth:`_engine.Result.fetchmany` except that 

1288 scalar values, rather than :class:`_engine.Row` objects, 

1289 are returned. 

1290 

1291 """ 

1292 return self._manyrow_getter(self, size) 

1293 

1294 def all(self) -> Sequence[_R]: 

1295 """Return all scalar values in a sequence. 

1296 

1297 Equivalent to :meth:`_engine.Result.all` except that 

1298 scalar values, rather than :class:`_engine.Row` objects, 

1299 are returned. 

1300 

1301 """ 

1302 return self._allrows() 

1303 

1304 def __iter__(self) -> Iterator[_R]: 

1305 return self._iter_impl() 

1306 

1307 def __next__(self) -> _R: 

1308 return self._next_impl() 

1309 

1310 def first(self) -> Optional[_R]: 

1311 """Fetch the first object or ``None`` if no object is present. 

1312 

1313 Equivalent to :meth:`_engine.Result.first` except that 

1314 scalar values, rather than :class:`_engine.Row` objects, 

1315 are returned. 

1316 

1317 

1318 """ 

1319 return self._only_one_row( 

1320 raise_for_second_row=False, raise_for_none=False, scalar=False 

1321 ) 

1322 

1323 def one_or_none(self) -> Optional[_R]: 

1324 """Return at most one object or raise an exception. 

1325 

1326 Equivalent to :meth:`_engine.Result.one_or_none` except that 

1327 scalar values, rather than :class:`_engine.Row` objects, 

1328 are returned. 

1329 

1330 """ 

1331 return self._only_one_row( 

1332 raise_for_second_row=True, raise_for_none=False, scalar=False 

1333 ) 

1334 

1335 def one(self) -> _R: 

1336 """Return exactly one object or raise an exception. 

1337 

1338 Equivalent to :meth:`_engine.Result.one` except that 

1339 scalar values, rather than :class:`_engine.Row` objects, 

1340 are returned. 

1341 

1342 """ 

1343 return self._only_one_row( 

1344 raise_for_second_row=True, raise_for_none=True, scalar=False 

1345 ) 

1346 

1347 

1348class TupleResult(FilterResult[_R], util.TypingOnly): 

1349 """A :class:`_engine.Result` that's typed as returning plain 

1350 Python tuples instead of rows. 

1351 

1352 Since :class:`_engine.Row` acts like a tuple in every way already, 

1353 this class is a typing only class, regular :class:`_engine.Result` is 

1354 still used at runtime. 

1355 

1356 """ 

1357 

1358 __slots__ = () 

1359 

1360 if TYPE_CHECKING: 

1361 

1362 def partitions( 

1363 self, size: Optional[int] = None 

1364 ) -> Iterator[Sequence[_R]]: 

1365 """Iterate through sub-lists of elements of the size given. 

1366 

1367 Equivalent to :meth:`_engine.Result.partitions` except that 

1368 tuple values, rather than :class:`_engine.Row` objects, 

1369 are returned. 

1370 

1371 """ 

1372 ... 

1373 

1374 def fetchone(self) -> Optional[_R]: 

1375 """Fetch one tuple. 

1376 

1377 Equivalent to :meth:`_engine.Result.fetchone` except that 

1378 tuple values, rather than :class:`_engine.Row` 

1379 objects, are returned. 

1380 

1381 """ 

1382 ... 

1383 

1384 def fetchall(self) -> Sequence[_R]: 

1385 """A synonym for the :meth:`_engine.ScalarResult.all` method.""" 

1386 ... 

1387 

1388 def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]: 

1389 """Fetch many objects. 

1390 

1391 Equivalent to :meth:`_engine.Result.fetchmany` except that 

1392 tuple values, rather than :class:`_engine.Row` objects, 

1393 are returned. 

1394 

1395 """ 

1396 ... 

1397 

1398 def all(self) -> Sequence[_R]: # noqa: A001 

1399 """Return all scalar values in a sequence. 

1400 

1401 Equivalent to :meth:`_engine.Result.all` except that 

1402 tuple values, rather than :class:`_engine.Row` objects, 

1403 are returned. 

1404 

1405 """ 

1406 ... 

1407 

1408 def __iter__(self) -> Iterator[_R]: ... 

1409 

1410 def __next__(self) -> _R: ... 

1411 

1412 def first(self) -> Optional[_R]: 

1413 """Fetch the first object or ``None`` if no object is present. 

1414 

1415 Equivalent to :meth:`_engine.Result.first` except that 

1416 tuple values, rather than :class:`_engine.Row` objects, 

1417 are returned. 

1418 

1419 

1420 """ 

1421 ... 

1422 

1423 def one_or_none(self) -> Optional[_R]: 

1424 """Return at most one object or raise an exception. 

1425 

1426 Equivalent to :meth:`_engine.Result.one_or_none` except that 

1427 tuple values, rather than :class:`_engine.Row` objects, 

1428 are returned. 

1429 

1430 """ 

1431 ... 

1432 

1433 def one(self) -> _R: 

1434 """Return exactly one object or raise an exception. 

1435 

1436 Equivalent to :meth:`_engine.Result.one` except that 

1437 tuple values, rather than :class:`_engine.Row` objects, 

1438 are returned. 

1439 

1440 """ 

1441 ... 

1442 

1443 @overload 

1444 def scalar_one(self: TupleResult[Tuple[_T]]) -> _T: ... 

1445 

1446 @overload 

1447 def scalar_one(self) -> Any: ... 

1448 

1449 def scalar_one(self) -> Any: 

1450 """Return exactly one scalar result or raise an exception. 

1451 

1452 This is equivalent to calling :meth:`_engine.Result.scalars` 

1453 and then :meth:`_engine.ScalarResult.one`. 

1454 

1455 .. seealso:: 

1456 

1457 :meth:`_engine.ScalarResult.one` 

1458 

1459 :meth:`_engine.Result.scalars` 

1460 

1461 """ 

1462 ... 

1463 

1464 @overload 

1465 def scalar_one_or_none( 

1466 self: TupleResult[Tuple[_T]], 

1467 ) -> Optional[_T]: ... 

1468 

1469 @overload 

1470 def scalar_one_or_none(self) -> Optional[Any]: ... 

1471 

1472 def scalar_one_or_none(self) -> Optional[Any]: 

1473 """Return exactly one or no scalar result. 

1474 

1475 This is equivalent to calling :meth:`_engine.Result.scalars` 

1476 and then :meth:`_engine.ScalarResult.one_or_none`. 

1477 

1478 .. seealso:: 

1479 

1480 :meth:`_engine.ScalarResult.one_or_none` 

1481 

1482 :meth:`_engine.Result.scalars` 

1483 

1484 """ 

1485 ... 

1486 

1487 @overload 

1488 def scalar(self: TupleResult[Tuple[_T]]) -> Optional[_T]: ... 

1489 

1490 @overload 

1491 def scalar(self) -> Any: ... 

1492 

1493 def scalar(self) -> Any: 

1494 """Fetch the first column of the first row, and close the result 

1495 set. 

1496 

1497 Returns ``None`` if there are no rows to fetch. 

1498 

1499 No validation is performed to test if additional rows remain. 

1500 

1501 After calling this method, the object is fully closed, 

1502 e.g. the :meth:`_engine.CursorResult.close` 

1503 method will have been called. 

1504 

1505 :return: a Python scalar value , or ``None`` if no rows remain. 

1506 

1507 """ 

1508 ... 

1509 

1510 

1511class MappingResult(_WithKeys, FilterResult[RowMapping]): 

1512 """A wrapper for a :class:`_engine.Result` that returns dictionary values 

1513 rather than :class:`_engine.Row` values. 

1514 

1515 The :class:`_engine.MappingResult` object is acquired by calling the 

1516 :meth:`_engine.Result.mappings` method. 

1517 

1518 """ 

1519 

1520 __slots__ = () 

1521 

1522 _generate_rows = True 

1523 

1524 _post_creational_filter = operator.attrgetter("_mapping") 

1525 

1526 def __init__(self, result: Result[Unpack[TupleAny]]): 

1527 self._real_result = result 

1528 self._unique_filter_state = result._unique_filter_state 

1529 self._metadata = result._metadata 

1530 if result._source_supports_scalars: 

1531 self._metadata = self._metadata._reduce([0]) 

1532 

1533 def unique(self, strategy: Optional[_UniqueFilterType] = None) -> Self: 

1534 """Apply unique filtering to the objects returned by this 

1535 :class:`_engine.MappingResult`. 

1536 

1537 See :meth:`_engine.Result.unique` for usage details. 

1538 

1539 """ 

1540 self._unique_filter_state = (set(), strategy) 

1541 return self 

1542 

1543 def columns(self, *col_expressions: _KeyIndexType) -> Self: 

1544 """Establish the columns that should be returned in each row.""" 

1545 return self._column_slices(col_expressions) 

1546 

1547 def partitions( 

1548 self, size: Optional[int] = None 

1549 ) -> Iterator[Sequence[RowMapping]]: 

1550 """Iterate through sub-lists of elements of the size given. 

1551 

1552 Equivalent to :meth:`_engine.Result.partitions` except that 

1553 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

1554 objects, are returned. 

1555 

1556 """ 

1557 

1558 getter = self._manyrow_getter 

1559 

1560 while True: 

1561 partition = getter(self, size) 

1562 if partition: 

1563 yield partition 

1564 else: 

1565 break 

1566 

1567 def fetchall(self) -> Sequence[RowMapping]: 

1568 """A synonym for the :meth:`_engine.MappingResult.all` method.""" 

1569 

1570 return self._allrows() 

1571 

1572 def fetchone(self) -> Optional[RowMapping]: 

1573 """Fetch one object. 

1574 

1575 Equivalent to :meth:`_engine.Result.fetchone` except that 

1576 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

1577 objects, are returned. 

1578 

1579 """ 

1580 

1581 row = self._onerow_getter(self) 

1582 if row is _NO_ROW: 

1583 return None 

1584 else: 

1585 return row 

1586 

1587 def fetchmany(self, size: Optional[int] = None) -> Sequence[RowMapping]: 

1588 """Fetch many objects. 

1589 

1590 Equivalent to :meth:`_engine.Result.fetchmany` except that 

1591 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

1592 objects, are returned. 

1593 

1594 """ 

1595 

1596 return self._manyrow_getter(self, size) 

1597 

1598 def all(self) -> Sequence[RowMapping]: 

1599 """Return all scalar values in a sequence. 

1600 

1601 Equivalent to :meth:`_engine.Result.all` except that 

1602 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

1603 objects, are returned. 

1604 

1605 """ 

1606 

1607 return self._allrows() 

1608 

1609 def __iter__(self) -> Iterator[RowMapping]: 

1610 return self._iter_impl() 

1611 

1612 def __next__(self) -> RowMapping: 

1613 return self._next_impl() 

1614 

1615 def first(self) -> Optional[RowMapping]: 

1616 """Fetch the first object or ``None`` if no object is present. 

1617 

1618 Equivalent to :meth:`_engine.Result.first` except that 

1619 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

1620 objects, are returned. 

1621 

1622 

1623 """ 

1624 return self._only_one_row( 

1625 raise_for_second_row=False, raise_for_none=False, scalar=False 

1626 ) 

1627 

1628 def one_or_none(self) -> Optional[RowMapping]: 

1629 """Return at most one object or raise an exception. 

1630 

1631 Equivalent to :meth:`_engine.Result.one_or_none` except that 

1632 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

1633 objects, are returned. 

1634 

1635 """ 

1636 return self._only_one_row( 

1637 raise_for_second_row=True, raise_for_none=False, scalar=False 

1638 ) 

1639 

1640 def one(self) -> RowMapping: 

1641 """Return exactly one object or raise an exception. 

1642 

1643 Equivalent to :meth:`_engine.Result.one` except that 

1644 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

1645 objects, are returned. 

1646 

1647 """ 

1648 return self._only_one_row( 

1649 raise_for_second_row=True, raise_for_none=True, scalar=False 

1650 ) 

1651 

1652 

1653class FrozenResult(Generic[Unpack[_Ts]]): 

1654 """Represents a :class:`_engine.Result` object in a "frozen" state suitable 

1655 for caching. 

1656 

1657 The :class:`_engine.FrozenResult` object is returned from the 

1658 :meth:`_engine.Result.freeze` method of any :class:`_engine.Result` 

1659 object. 

1660 

1661 A new iterable :class:`_engine.Result` object is generated from a fixed 

1662 set of data each time the :class:`_engine.FrozenResult` is invoked as 

1663 a callable:: 

1664 

1665 

1666 result = connection.execute(query) 

1667 

1668 frozen = result.freeze() 

1669 

1670 unfrozen_result_one = frozen() 

1671 

1672 for row in unfrozen_result_one: 

1673 print(row) 

1674 

1675 unfrozen_result_two = frozen() 

1676 rows = unfrozen_result_two.all() 

1677 

1678 # ... etc 

1679 

1680 .. versionadded:: 1.4 

1681 

1682 .. seealso:: 

1683 

1684 :ref:`do_orm_execute_re_executing` - example usage within the 

1685 ORM to implement a result-set cache. 

1686 

1687 :func:`_orm.loading.merge_frozen_result` - ORM function to merge 

1688 a frozen result back into a :class:`_orm.Session`. 

1689 

1690 """ 

1691 

1692 data: Sequence[Any] 

1693 

1694 def __init__(self, result: Result[Unpack[_Ts]]): 

1695 self.metadata = result._metadata._for_freeze() 

1696 self._source_supports_scalars = result._source_supports_scalars 

1697 self._attributes = result._attributes 

1698 

1699 if self._source_supports_scalars: 

1700 self.data = list(result._raw_row_iterator()) 

1701 else: 

1702 self.data = result.fetchall() 

1703 

1704 def _rewrite_rows(self) -> Sequence[Sequence[Any]]: 

1705 # used only by the orm fn merge_frozen_result 

1706 if self._source_supports_scalars: 

1707 return [[elem] for elem in self.data] 

1708 else: 

1709 return [list(row) for row in self.data] 

1710 

1711 def with_new_rows( 

1712 self, tuple_data: Sequence[Row[Unpack[_Ts]]] 

1713 ) -> FrozenResult[Unpack[_Ts]]: 

1714 fr = FrozenResult.__new__(FrozenResult) 

1715 fr.metadata = self.metadata 

1716 fr._attributes = self._attributes 

1717 fr._source_supports_scalars = self._source_supports_scalars 

1718 

1719 if self._source_supports_scalars: 

1720 fr.data = [d[0] for d in tuple_data] # type: ignore[misc] 

1721 else: 

1722 fr.data = tuple_data 

1723 return fr 

1724 

1725 def __call__(self) -> Result[Unpack[_Ts]]: 

1726 result: IteratorResult[Unpack[_Ts]] = IteratorResult( 

1727 self.metadata, iter(self.data) 

1728 ) 

1729 result._attributes = self._attributes 

1730 result._source_supports_scalars = self._source_supports_scalars 

1731 return result 

1732 

1733 

1734class IteratorResult(Result[Unpack[_Ts]]): 

1735 """A :class:`_engine.Result` that gets data from a Python iterator of 

1736 :class:`_engine.Row` objects or similar row-like data. 

1737 

1738 .. versionadded:: 1.4 

1739 

1740 """ 

1741 

1742 _hard_closed = False 

1743 _soft_closed = False 

1744 

1745 def __init__( 

1746 self, 

1747 cursor_metadata: ResultMetaData, 

1748 iterator: Iterator[_InterimSupportsScalarsRowType], 

1749 raw: Optional[Result[Any]] = None, 

1750 _source_supports_scalars: bool = False, 

1751 ): 

1752 self._metadata = cursor_metadata 

1753 self.iterator = iterator 

1754 self.raw = raw 

1755 self._source_supports_scalars = _source_supports_scalars 

1756 

1757 @property 

1758 def closed(self) -> bool: 

1759 """Return ``True`` if this :class:`_engine.IteratorResult` has 

1760 been closed 

1761 

1762 .. versionadded:: 1.4.43 

1763 

1764 """ 

1765 return self._hard_closed 

1766 

1767 def _soft_close(self, hard: bool = False, **kw: Any) -> None: 

1768 if hard: 

1769 self._hard_closed = True 

1770 if self.raw is not None: 

1771 self.raw._soft_close(hard=hard, **kw) 

1772 self.iterator = iter([]) 

1773 self._reset_memoizations() 

1774 self._soft_closed = True 

1775 

1776 def _raise_hard_closed(self) -> NoReturn: 

1777 raise exc.ResourceClosedError("This result object is closed.") 

1778 

1779 def _raw_row_iterator(self) -> Iterator[_RowData]: 

1780 return self.iterator 

1781 

1782 def _fetchiter_impl(self) -> Iterator[_InterimSupportsScalarsRowType]: 

1783 if self._hard_closed: 

1784 self._raise_hard_closed() 

1785 return self.iterator 

1786 

1787 def _fetchone_impl( 

1788 self, hard_close: bool = False 

1789 ) -> Optional[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1790 if self._hard_closed: 

1791 self._raise_hard_closed() 

1792 

1793 row = next(self.iterator, _NO_ROW) 

1794 if row is _NO_ROW: 

1795 self._soft_close(hard=hard_close) 

1796 return None 

1797 else: 

1798 return row 

1799 

1800 def _fetchall_impl( 

1801 self, 

1802 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1803 if self._hard_closed: 

1804 self._raise_hard_closed() 

1805 try: 

1806 return list(self.iterator) 

1807 finally: 

1808 self._soft_close() 

1809 

1810 def _fetchmany_impl( 

1811 self, size: Optional[int] = None 

1812 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1813 if self._hard_closed: 

1814 self._raise_hard_closed() 

1815 

1816 return list(itertools.islice(self.iterator, 0, size)) 

1817 

1818 

1819def null_result() -> IteratorResult[Any]: 

1820 return IteratorResult(SimpleResultMetaData([]), iter([])) 

1821 

1822 

1823class ChunkedIteratorResult(IteratorResult[Unpack[_Ts]]): 

1824 """An :class:`_engine.IteratorResult` that works from an 

1825 iterator-producing callable. 

1826 

1827 The given ``chunks`` argument is a function that is given a number of rows 

1828 to return in each chunk, or ``None`` for all rows. The function should 

1829 then return an un-consumed iterator of lists, each list of the requested 

1830 size. 

1831 

1832 The function can be called at any time again, in which case it should 

1833 continue from the same result set but adjust the chunk size as given. 

1834 

1835 .. versionadded:: 1.4 

1836 

1837 """ 

1838 

1839 def __init__( 

1840 self, 

1841 cursor_metadata: ResultMetaData, 

1842 chunks: Callable[ 

1843 [Optional[int]], Iterator[Sequence[_InterimRowType[_R]]] 

1844 ], 

1845 source_supports_scalars: bool = False, 

1846 raw: Optional[Result[Any]] = None, 

1847 dynamic_yield_per: bool = False, 

1848 ): 

1849 self._metadata = cursor_metadata 

1850 self.chunks = chunks 

1851 self._source_supports_scalars = source_supports_scalars 

1852 self.raw = raw 

1853 self.iterator = itertools.chain.from_iterable(self.chunks(None)) 

1854 self.dynamic_yield_per = dynamic_yield_per 

1855 

1856 @_generative 

1857 def yield_per(self, num: int) -> Self: 

1858 # TODO: this throws away the iterator which may be holding 

1859 # onto a chunk. the yield_per cannot be changed once any 

1860 # rows have been fetched. either find a way to enforce this, 

1861 # or we can't use itertools.chain and will instead have to 

1862 # keep track. 

1863 

1864 self._yield_per = num 

1865 self.iterator = itertools.chain.from_iterable(self.chunks(num)) 

1866 return self 

1867 

1868 def _soft_close(self, hard: bool = False, **kw: Any) -> None: 

1869 super()._soft_close(hard=hard, **kw) 

1870 self.chunks = lambda size: [] # type: ignore 

1871 

1872 def _fetchmany_impl( 

1873 self, size: Optional[int] = None 

1874 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1875 if self.dynamic_yield_per: 

1876 self.iterator = itertools.chain.from_iterable(self.chunks(size)) 

1877 return super()._fetchmany_impl(size=size) 

1878 

1879 

1880class MergedResult(IteratorResult[Unpack[_Ts]]): 

1881 """A :class:`_engine.Result` that is merged from any number of 

1882 :class:`_engine.Result` objects. 

1883 

1884 Returned by the :meth:`_engine.Result.merge` method. 

1885 

1886 .. versionadded:: 1.4 

1887 

1888 """ 

1889 

1890 closed = False 

1891 rowcount: Optional[int] 

1892 

1893 def __init__( 

1894 self, 

1895 cursor_metadata: ResultMetaData, 

1896 results: Sequence[Result[Unpack[_Ts]]], 

1897 ): 

1898 self._results = results 

1899 super().__init__( 

1900 cursor_metadata, 

1901 itertools.chain.from_iterable( 

1902 r._raw_row_iterator() for r in results 

1903 ), 

1904 ) 

1905 

1906 self._unique_filter_state = results[0]._unique_filter_state 

1907 self._yield_per = results[0]._yield_per 

1908 

1909 # going to try something w/ this in next rev 

1910 self._source_supports_scalars = results[0]._source_supports_scalars 

1911 

1912 self._attributes = self._attributes.merge_with( 

1913 *[r._attributes for r in results] 

1914 ) 

1915 

1916 def _soft_close(self, hard: bool = False, **kw: Any) -> None: 

1917 for r in self._results: 

1918 r._soft_close(hard=hard, **kw) 

1919 if hard: 

1920 self.closed = True