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

581 statements  

1# engine/result.py 

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

51from ..util.typing import Self 

52from ..util.typing import TupleAny 

53from ..util.typing import TypeVarTuple 

54from ..util.typing import Unpack 

55 

56if typing.TYPE_CHECKING: 

57 from typing import Type 

58 

59 from .. import inspection 

60 from ..sql import roles 

61 from ..sql._typing import _HasClauseElement 

62 from ..sql.elements import SQLCoreOperations 

63 from ..sql.type_api import _ResultProcessorType 

64 

65_KeyType = Union[ 

66 str, 

67 "SQLCoreOperations[Any]", 

68 "roles.TypedColumnsClauseRole[Any]", 

69 "roles.ColumnsClauseRole", 

70 "Type[Any]", 

71 "inspection.Inspectable[_HasClauseElement[Any]]", 

72] 

73_KeyIndexType = Union[_KeyType, int] 

74 

75# is overridden in cursor using _CursorKeyMapRecType 

76_KeyMapRecType = Any 

77 

78_KeyMapType = Mapping[_KeyType, _KeyMapRecType] 

79 

80 

81_Ts = TypeVarTuple("_Ts") 

82 

83 

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

85 

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

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

88 

89 

90class ResultMetaData: 

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

92 

93 __slots__ = () 

94 

95 _tuplefilter: Optional[_TupleGetterType] = None 

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

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

98 _keymap: _KeyMapType 

99 _keys: Sequence[str] 

100 _processors: Optional[_ProcessorsType] 

101 _key_to_index: Dict[_KeyType, int] 

102 

103 @property 

104 def keys(self) -> RMKeyView: 

105 return RMKeyView(self) 

106 

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

108 raise NotImplementedError() 

109 

110 def _for_freeze(self) -> ResultMetaData: 

111 raise NotImplementedError() 

112 

113 @overload 

114 def _key_fallback( 

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

116 ) -> NoReturn: ... 

117 

118 @overload 

119 def _key_fallback( 

120 self, 

121 key: Any, 

122 err: Optional[Exception], 

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

124 ) -> None: ... 

125 

126 @overload 

127 def _key_fallback( 

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

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

130 

131 def _key_fallback( 

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

133 ) -> Optional[NoReturn]: 

134 assert raiseerr 

135 raise KeyError(key) from err 

136 

137 def _raise_for_ambiguous_column_name( 

138 self, rec: _KeyMapRecType 

139 ) -> NoReturn: 

140 raise NotImplementedError( 

141 "ambiguous column name logic is implemented for " 

142 "CursorResultMetaData" 

143 ) 

144 

145 def _index_for_key( 

146 self, key: _KeyIndexType, raiseerr: bool 

147 ) -> Optional[int]: 

148 raise NotImplementedError() 

149 

150 def _indexes_for_keys( 

151 self, keys: Sequence[_KeyIndexType] 

152 ) -> Sequence[int]: 

153 raise NotImplementedError() 

154 

155 def _metadata_for_keys( 

156 self, keys: Sequence[_KeyIndexType] 

157 ) -> Iterator[_KeyMapRecType]: 

158 raise NotImplementedError() 

159 

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

161 raise NotImplementedError() 

162 

163 def _getter( 

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

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

166 index = self._index_for_key(key, raiseerr) 

167 

168 if index is not None: 

169 return operator.itemgetter(index) 

170 else: 

171 return None 

172 

173 def _row_as_tuple_getter( 

174 self, keys: Sequence[_KeyIndexType] 

175 ) -> _TupleGetterType: 

176 indexes = self._indexes_for_keys(keys) 

177 return tuplegetter(*indexes) 

178 

179 def _make_key_to_index( 

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

181 ) -> Dict[_KeyType, int]: 

182 return { 

183 key: rec[index] 

184 for key, rec in keymap.items() 

185 if rec[index] is not None 

186 } 

187 

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

189 if key in self._keymap: 

190 # the index must be none in this case 

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

192 else: 

193 # unknown key 

194 if attr_error: 

195 try: 

196 self._key_fallback(key, None) 

197 except KeyError as ke: 

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

199 else: 

200 self._key_fallback(key, None) 

201 

202 @property 

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

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

205 return None 

206 else: 

207 return self._processors 

208 

209 

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

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

212 

213 _parent: ResultMetaData 

214 _keys: Sequence[str] 

215 

216 def __init__(self, parent: ResultMetaData): 

217 self._parent = parent 

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

219 

220 def __len__(self) -> int: 

221 return len(self._keys) 

222 

223 def __repr__(self) -> str: 

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

225 

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

227 return iter(self._keys) 

228 

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

230 if isinstance(item, int): 

231 return False 

232 

233 # note this also includes special key fallback behaviors 

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

235 return self._parent._has_key(item) 

236 

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

238 return list(other) == list(self) 

239 

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

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

242 

243 

244class SimpleResultMetaData(ResultMetaData): 

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

246 

247 __slots__ = ( 

248 "_keys", 

249 "_keymap", 

250 "_processors", 

251 "_tuplefilter", 

252 "_translated_indexes", 

253 "_unique_filters", 

254 "_key_to_index", 

255 ) 

256 

257 _keys: Sequence[str] 

258 

259 def __init__( 

260 self, 

261 keys: Sequence[str], 

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

263 _processors: Optional[_ProcessorsType] = None, 

264 _tuplefilter: Optional[_TupleGetterType] = None, 

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

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

267 ): 

268 self._keys = list(keys) 

269 self._tuplefilter = _tuplefilter 

270 self._translated_indexes = _translated_indexes 

271 self._unique_filters = _unique_filters 

272 if extra: 

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

274 recs_names = [ 

275 ( 

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

277 (index, name, extras), 

278 ) 

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

280 ] 

281 else: 

282 recs_names = [ 

283 ((name,), (index, name, ())) 

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

285 ] 

286 

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

288 

289 self._processors = _processors 

290 

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

292 

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

294 return key in self._keymap 

295 

296 def _for_freeze(self) -> ResultMetaData: 

297 unique_filters = self._unique_filters 

298 if unique_filters and self._tuplefilter: 

299 unique_filters = self._tuplefilter(unique_filters) 

300 

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

302 # applied? 

303 return SimpleResultMetaData( 

304 self._keys, 

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

306 _unique_filters=unique_filters, 

307 ) 

308 

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

310 return { 

311 "_keys": self._keys, 

312 "_translated_indexes": self._translated_indexes, 

313 } 

314 

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

316 if state["_translated_indexes"]: 

317 _translated_indexes = state["_translated_indexes"] 

318 _tuplefilter = tuplegetter(*_translated_indexes) 

319 else: 

320 _translated_indexes = _tuplefilter = None 

321 self.__init__( # type: ignore 

322 state["_keys"], 

323 _translated_indexes=_translated_indexes, 

324 _tuplefilter=_tuplefilter, 

325 ) 

326 

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

328 if isinstance(key, int): 

329 key = self._keys[key] 

330 try: 

331 rec = self._keymap[key] 

332 except KeyError as ke: 

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

334 

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

336 

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

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

339 

340 def _metadata_for_keys( 

341 self, keys: Sequence[Any] 

342 ) -> Iterator[_KeyMapRecType]: 

343 for key in keys: 

344 if isinstance(key, int): 

345 key = self._keys[key] 

346 

347 try: 

348 rec = self._keymap[key] 

349 except KeyError as ke: 

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

351 

352 yield rec 

353 

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

355 try: 

356 metadata_for_keys = [ 

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

358 for key in keys 

359 ] 

360 except KeyError as ke: 

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

362 

363 indexes: Sequence[int] 

364 new_keys: Sequence[str] 

365 extra: Sequence[Any] 

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

367 

368 if self._translated_indexes: 

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

370 

371 tup = tuplegetter(*indexes) 

372 

373 new_metadata = SimpleResultMetaData( 

374 new_keys, 

375 extra=extra, 

376 _tuplefilter=tup, 

377 _translated_indexes=indexes, 

378 _processors=self._processors, 

379 _unique_filters=self._unique_filters, 

380 ) 

381 

382 return new_metadata 

383 

384 

385def result_tuple( 

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

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

388 parent = SimpleResultMetaData(fields, extra) 

389 return functools.partial( 

390 Row, parent, parent._effective_processors, parent._key_to_index 

391 ) 

392 

393 

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

395 __slots__ = () 

396 _is_cursor = False 

397 

398 @_generative 

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

400 real_result = ( 

401 self._real_result 

402 if self._real_result 

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

404 ) 

405 

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

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

408 

409 assert self._generate_rows 

410 

411 return self 

412 

413 

414class _WithKeys: 

415 __slots__ = () 

416 

417 _metadata: ResultMetaData 

418 

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

420 def keys(self) -> RMKeyView: 

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

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

423 

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

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

426 execution. 

427 

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

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

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

431 

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

433 plain list. 

434 

435 

436 """ 

437 return self._metadata.keys 

438 

439 

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

441 """Represent a set of database results. 

442 

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

444 completely updated usage model and calling facade for SQLAlchemy 

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

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

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

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

449 is normally used. 

450 

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

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

453 yield instances of ORM mapped objects either individually or within 

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

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

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

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

458 method. 

459 

460 .. seealso:: 

461 

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

463 

464 """ 

465 

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

467 

468 _row_logging_fn: Optional[ 

469 Callable[[Row[Unpack[TupleAny]]], Row[Unpack[TupleAny]]] 

470 ] = None 

471 

472 _source_supports_scalars: bool = False 

473 

474 _yield_per: Optional[int] = None 

475 

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

477 

478 def __init__(self, cursor_metadata: ResultMetaData): 

479 self._metadata = cursor_metadata 

480 

481 def __enter__(self) -> Self: 

482 return self 

483 

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

485 self.close() 

486 

487 def close(self) -> None: 

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

489 

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

491 not implemented by default. The method should generally end 

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

493 subsequent iteration or row fetching to raise 

494 :class:`.ResourceClosedError`. 

495 

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

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

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

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

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

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

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

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

504 exhaust and autoclose the database cursor. 

505 

506 """ 

507 self._soft_close(hard=True) 

508 

509 @property 

510 def _soft_closed(self) -> bool: 

511 raise NotImplementedError() 

512 

513 @property 

514 def closed(self) -> bool: 

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

516 

517 .. versionadded:: 1.4.43 

518 

519 """ 

520 raise NotImplementedError() 

521 

522 @_generative 

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

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

525 

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

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

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

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

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

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

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

533 rows remain if fewer remain. 

534 

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

536 conjunction with the 

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

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

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

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

541 

542 .. tip:: 

543 

544 Consider using the 

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

546 execution option, which will simultaneously set 

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

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

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

550 a fixed row buffer size at once. 

551 

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

553 execution option is available for ORM operations, with 

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

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

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

557 

558 .. versionadded:: 1.4 

559 

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

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

562 

563 .. seealso:: 

564 

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

566 :meth:`_engine.Result.yield_per` 

567 

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

569 

570 """ 

571 self._yield_per = num 

572 return self 

573 

574 @_generative 

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

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

577 :class:`_engine.Result`. 

578 

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

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

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

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

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

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

585 returned objects. 

586 

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

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

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

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

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

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

593 

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

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

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

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

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

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

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

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

602 of the requested size. 

603 

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

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

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

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

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

609 :class:`_engine.Result` object. 

610 

611 """ 

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

613 return self 

614 

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

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

617 

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

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

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

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

622 a given statement construct. 

623 

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

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

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

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

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

629 has been corrected such that calling upon 

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

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

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

633 only a single column. 

634 

635 E.g.:: 

636 

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

638 result = connection.execute(statement) 

639 

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

641 ... 

642 

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

644 

645 for z, y in result.columns( 

646 statement.selected_columns.c.z, statement.selected_columns.c.y 

647 ): 

648 ... 

649 

650 .. versionadded:: 1.4 

651 

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

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

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

655 

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

657 given. 

658 

659 """ 

660 return self._column_slices(col_expressions) 

661 

662 @overload 

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

664 

665 @overload 

666 def scalars( 

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

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

669 

670 @overload 

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

672 

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

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

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

676 

677 E.g.:: 

678 

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

680 >>> result.scalars().all() 

681 [1, 2, 3] 

682 

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

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

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

686 

687 .. versionadded:: 1.4 

688 

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

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

691 

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

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

694 

695 """ 

696 return ScalarResult(self, index) 

697 

698 def _getter( 

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

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

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

702 :class:`_engine.Row`. 

703 

704 """ 

705 if self._source_supports_scalars: 

706 raise NotImplementedError( 

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

708 ) 

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

710 

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

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

713 :class:`_engine.Row`. 

714 

715 """ 

716 if self._source_supports_scalars: 

717 raise NotImplementedError( 

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

719 ) 

720 return self._metadata._row_as_tuple_getter(keys) 

721 

722 def mappings(self) -> MappingResult: 

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

724 :class:`_engine.MappingResult`. 

725 

726 When this filter is applied, fetching rows will return 

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

728 objects. 

729 

730 .. versionadded:: 1.4 

731 

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

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

734 

735 """ 

736 

737 return MappingResult(self) 

738 

739 @property 

740 @deprecated( 

741 "2.1.0", 

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

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

744 ) 

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

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

747 

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

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

750 

751 .. versionadded:: 2.0 

752 

753 .. seealso:: 

754 

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

756 workaround for SQLAlchemy 2.1. 

757 

758 """ 

759 return self # type: ignore 

760 

761 @deprecated( 

762 "2.1.0", 

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

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

765 ) 

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

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

768 

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

770 at runtime, 

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

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

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

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

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

776 itself included typing information. 

777 

778 .. versionadded:: 2.0 

779 

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

781 

782 .. seealso:: 

783 

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

785 workaround for SQLAlchemy 2.1. 

786 

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

788 

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

790 

791 """ 

792 

793 return self # type: ignore 

794 

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

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

797 

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

799 to merge multiple compatible results together. 

800 

801 """ 

802 raise NotImplementedError() 

803 

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

805 return self._iter_impl() 

806 

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

808 return self._next_impl() 

809 

810 def partitions( 

811 self, size: Optional[int] = None 

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

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

814 

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

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

817 lists will be yielded. 

818 

819 The result object is automatically closed when the iterator 

820 is fully consumed. 

821 

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

823 ahead of time unless the 

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

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

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

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

828 

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

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

831 combined with use of the 

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

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

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

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

836 yielding them out. 

837 

838 .. versionadded:: 1.4 

839 

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

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

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

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

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

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

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

847 specific and not well defined. 

848 

849 :return: iterator of lists 

850 

851 .. seealso:: 

852 

853 :ref:`engine_stream_results` 

854 

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

856 

857 """ 

858 

859 getter = self._manyrow_getter 

860 

861 while True: 

862 partition = getter(self, size) 

863 if partition: 

864 yield partition 

865 else: 

866 break 

867 

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

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

870 

871 return self._allrows() 

872 

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

874 """Fetch one row. 

875 

876 When all rows are exhausted, returns None. 

877 

878 This method is provided for backwards compatibility with 

879 SQLAlchemy 1.x.x. 

880 

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

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

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

884 

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

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

887 

888 """ 

889 row = self._onerow_getter(self) 

890 if row is _NO_ROW: 

891 return None 

892 else: 

893 return row 

894 

895 def fetchmany( 

896 self, size: Optional[int] = None 

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

898 """Fetch many rows. 

899 

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

901 

902 This method is provided for backwards compatibility with 

903 SQLAlchemy 1.x.x. 

904 

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

906 method. 

907 

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

909 

910 .. seealso:: 

911 

912 :meth:`_engine.Result.partitions` 

913 

914 """ 

915 

916 return self._manyrow_getter(self, size) 

917 

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

919 """Return all rows in a sequence. 

920 

921 Closes the result set after invocation. Subsequent invocations 

922 will return an empty sequence. 

923 

924 .. versionadded:: 1.4 

925 

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

927 

928 .. seealso:: 

929 

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

931 without loading it completely in python. 

932 

933 """ 

934 

935 return self._allrows() 

936 

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

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

939 

940 Closes the result set and discards remaining rows. 

941 

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

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

944 column of the first row, use the 

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

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

947 :meth:`_engine.Result.first`. 

948 

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

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

951 SQL query which was invoked to produce this 

952 :class:`_engine.Result`; 

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

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

955 the first row will be discarded. 

956 

957 .. seealso:: 

958 

959 :ref:`migration_20_unify_select` 

960 

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

962 if no rows remain. 

963 

964 .. seealso:: 

965 

966 :meth:`_engine.Result.scalar` 

967 

968 :meth:`_engine.Result.one` 

969 

970 """ 

971 

972 return self._only_one_row( 

973 raise_for_second_row=False, raise_for_none=False, scalar=False 

974 ) 

975 

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

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

978 

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

980 Raises :class:`.MultipleResultsFound` 

981 if multiple rows are returned. 

982 

983 .. versionadded:: 1.4 

984 

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

986 is available. 

987 

988 :raises: :class:`.MultipleResultsFound` 

989 

990 .. seealso:: 

991 

992 :meth:`_engine.Result.first` 

993 

994 :meth:`_engine.Result.one` 

995 

996 """ 

997 return self._only_one_row( 

998 raise_for_second_row=True, raise_for_none=False, scalar=False 

999 ) 

1000 

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

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

1003 

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

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

1006 

1007 .. seealso:: 

1008 

1009 :meth:`_engine.ScalarResult.one` 

1010 

1011 :meth:`_engine.Result.scalars` 

1012 

1013 """ 

1014 return self._only_one_row( 

1015 raise_for_second_row=True, raise_for_none=True, scalar=True 

1016 ) 

1017 

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

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

1020 

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

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

1023 

1024 .. seealso:: 

1025 

1026 :meth:`_engine.ScalarResult.one_or_none` 

1027 

1028 :meth:`_engine.Result.scalars` 

1029 

1030 """ 

1031 return self._only_one_row( 

1032 raise_for_second_row=True, raise_for_none=False, scalar=True 

1033 ) 

1034 

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

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

1037 

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

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

1040 would be returned. 

1041 

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

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

1044 column of the first row, use the 

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

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

1047 :meth:`_engine.Result.one`. 

1048 

1049 .. versionadded:: 1.4 

1050 

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

1052 

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

1054 

1055 .. seealso:: 

1056 

1057 :meth:`_engine.Result.first` 

1058 

1059 :meth:`_engine.Result.one_or_none` 

1060 

1061 :meth:`_engine.Result.scalar_one` 

1062 

1063 """ 

1064 return self._only_one_row( 

1065 raise_for_second_row=True, raise_for_none=True, scalar=False 

1066 ) 

1067 

1068 # special case to handle mypy issue: 

1069 # https://github.com/python/mypy/issues/20651 

1070 @overload 

1071 def scalar(self: Result[Never, Unpack[TupleAny]]) -> Optional[Any]: 

1072 pass 

1073 

1074 @overload 

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

1076 pass 

1077 

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

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

1080 

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

1082 

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

1084 

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

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

1087 method will have been called. 

1088 

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

1090 

1091 """ 

1092 return self._only_one_row( 

1093 raise_for_second_row=False, raise_for_none=False, scalar=True 

1094 ) 

1095 

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

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

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

1099 

1100 The callable object returned is an instance of 

1101 :class:`_engine.FrozenResult`. 

1102 

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

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

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

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

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

1108 against its stored set of rows. 

1109 

1110 .. seealso:: 

1111 

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

1113 ORM to implement a result-set cache. 

1114 

1115 """ 

1116 

1117 return FrozenResult(self) 

1118 

1119 def merge( 

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

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

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

1123 objects. 

1124 

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

1126 which will be composed of iterators from the given result 

1127 objects. 

1128 

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

1130 The subsequent result objects must be against an identical 

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

1132 undefined. 

1133 

1134 """ 

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

1136 

1137 

1138class FilterResult(ResultInternal[_R]): 

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

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

1141 

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

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

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

1145 

1146 """ 

1147 

1148 __slots__ = ( 

1149 "_real_result", 

1150 "_post_creational_filter", 

1151 "_metadata", 

1152 "_unique_filter_state", 

1153 "__dict__", 

1154 ) 

1155 

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

1157 

1158 _real_result: Result[Unpack[TupleAny]] 

1159 

1160 def __enter__(self) -> Self: 

1161 return self 

1162 

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

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

1165 

1166 @_generative 

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

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

1169 

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

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

1172 documentation for usage notes. 

1173 

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

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

1176 

1177 .. seealso:: 

1178 

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

1180 :meth:`_engine.Result.yield_per` 

1181 

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

1183 

1184 """ 

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

1186 return self 

1187 

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

1189 self._real_result._soft_close(hard=hard) 

1190 

1191 @property 

1192 def _soft_closed(self) -> bool: 

1193 return self._real_result._soft_closed 

1194 

1195 @property 

1196 def closed(self) -> bool: 

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

1198 closed 

1199 

1200 .. versionadded:: 1.4.43 

1201 

1202 """ 

1203 return self._real_result.closed 

1204 

1205 def close(self) -> None: 

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

1207 

1208 .. versionadded:: 1.4.43 

1209 

1210 """ 

1211 self._real_result.close() 

1212 

1213 @property 

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

1215 return self._real_result._attributes 

1216 

1217 def _fetchiter_impl( 

1218 self, 

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

1220 return self._real_result._fetchiter_impl() 

1221 

1222 def _fetchone_impl( 

1223 self, hard_close: bool = False 

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

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

1226 

1227 def _fetchall_impl( 

1228 self, 

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

1230 return self._real_result._fetchall_impl() 

1231 

1232 def _fetchmany_impl( 

1233 self, size: Optional[int] = None 

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

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

1236 

1237 

1238class ScalarResult(FilterResult[_R]): 

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

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

1241 

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

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

1244 

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

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

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

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

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

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

1251 

1252 """ 

1253 

1254 __slots__ = () 

1255 

1256 _generate_rows = False 

1257 

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

1259 

1260 def __init__( 

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

1262 ): 

1263 self._real_result = real_result 

1264 

1265 if real_result._source_supports_scalars: 

1266 self._metadata = real_result._metadata 

1267 self._post_creational_filter = None 

1268 else: 

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

1270 self._post_creational_filter = operator.itemgetter(0) 

1271 

1272 self._unique_filter_state = real_result._unique_filter_state 

1273 

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

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

1276 :class:`_engine.ScalarResult`. 

1277 

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

1279 

1280 """ 

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

1282 return self 

1283 

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

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

1286 

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

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

1289 are returned. 

1290 

1291 """ 

1292 

1293 getter = self._manyrow_getter 

1294 

1295 while True: 

1296 partition = getter(self, size) 

1297 if partition: 

1298 yield partition 

1299 else: 

1300 break 

1301 

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

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

1304 

1305 return self._allrows() 

1306 

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

1308 """Fetch many objects. 

1309 

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

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

1312 are returned. 

1313 

1314 """ 

1315 return self._manyrow_getter(self, size) 

1316 

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

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

1319 

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

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

1322 are returned. 

1323 

1324 """ 

1325 return self._allrows() 

1326 

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

1328 return self._iter_impl() 

1329 

1330 def __next__(self) -> _R: 

1331 return self._next_impl() 

1332 

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

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

1335 

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

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

1338 are returned. 

1339 

1340 

1341 """ 

1342 return self._only_one_row( 

1343 raise_for_second_row=False, raise_for_none=False, scalar=False 

1344 ) 

1345 

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

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

1348 

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

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

1351 are returned. 

1352 

1353 """ 

1354 return self._only_one_row( 

1355 raise_for_second_row=True, raise_for_none=False, scalar=False 

1356 ) 

1357 

1358 def one(self) -> _R: 

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

1360 

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

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

1363 are returned. 

1364 

1365 """ 

1366 return self._only_one_row( 

1367 raise_for_second_row=True, raise_for_none=True, scalar=False 

1368 ) 

1369 

1370 

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

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

1373 Python tuples instead of rows. 

1374 

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

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

1377 still used at runtime. 

1378 

1379 """ 

1380 

1381 __slots__ = () 

1382 

1383 if TYPE_CHECKING: 

1384 

1385 def partitions( 

1386 self, size: Optional[int] = None 

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

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

1389 

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

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

1392 are returned. 

1393 

1394 """ 

1395 ... 

1396 

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

1398 """Fetch one tuple. 

1399 

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

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

1402 objects, are returned. 

1403 

1404 """ 

1405 ... 

1406 

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

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

1409 ... 

1410 

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

1412 """Fetch many objects. 

1413 

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

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

1416 are returned. 

1417 

1418 """ 

1419 ... 

1420 

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

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

1423 

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

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

1426 are returned. 

1427 

1428 """ 

1429 ... 

1430 

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

1432 

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

1434 

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

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

1437 

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

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

1440 are returned. 

1441 

1442 

1443 """ 

1444 ... 

1445 

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

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

1448 

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

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

1451 are returned. 

1452 

1453 """ 

1454 ... 

1455 

1456 def one(self) -> _R: 

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

1458 

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

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

1461 are returned. 

1462 

1463 """ 

1464 ... 

1465 

1466 @overload 

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

1468 

1469 @overload 

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

1471 

1472 def scalar_one(self) -> Any: 

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

1474 

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

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

1477 

1478 .. seealso:: 

1479 

1480 :meth:`_engine.ScalarResult.one` 

1481 

1482 :meth:`_engine.Result.scalars` 

1483 

1484 """ 

1485 ... 

1486 

1487 @overload 

1488 def scalar_one_or_none( 

1489 self: TupleResult[Tuple[_T]], 

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

1491 

1492 @overload 

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

1494 

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

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

1497 

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

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

1500 

1501 .. seealso:: 

1502 

1503 :meth:`_engine.ScalarResult.one_or_none` 

1504 

1505 :meth:`_engine.Result.scalars` 

1506 

1507 """ 

1508 ... 

1509 

1510 @overload 

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

1512 

1513 @overload 

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

1515 

1516 def scalar(self) -> Any: 

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

1518 set. 

1519 

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

1521 

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

1523 

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

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

1526 method will have been called. 

1527 

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

1529 

1530 """ 

1531 ... 

1532 

1533 

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

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

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

1537 

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

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

1540 

1541 """ 

1542 

1543 __slots__ = () 

1544 

1545 _generate_rows = True 

1546 

1547 _post_creational_filter = operator.attrgetter("_mapping") 

1548 

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

1550 self._real_result = result 

1551 self._unique_filter_state = result._unique_filter_state 

1552 self._metadata = result._metadata 

1553 if result._source_supports_scalars: 

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

1555 

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

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

1558 :class:`_engine.MappingResult`. 

1559 

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

1561 

1562 """ 

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

1564 return self 

1565 

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

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

1568 return self._column_slices(col_expressions) 

1569 

1570 def partitions( 

1571 self, size: Optional[int] = None 

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

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

1574 

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

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

1577 objects, are returned. 

1578 

1579 """ 

1580 

1581 getter = self._manyrow_getter 

1582 

1583 while True: 

1584 partition = getter(self, size) 

1585 if partition: 

1586 yield partition 

1587 else: 

1588 break 

1589 

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

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

1592 

1593 return self._allrows() 

1594 

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

1596 """Fetch one object. 

1597 

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

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

1600 objects, are returned. 

1601 

1602 """ 

1603 

1604 row = self._onerow_getter(self) 

1605 if row is _NO_ROW: 

1606 return None 

1607 else: 

1608 return row 

1609 

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

1611 """Fetch many objects. 

1612 

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

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

1615 objects, are returned. 

1616 

1617 """ 

1618 

1619 return self._manyrow_getter(self, size) 

1620 

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

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

1623 

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

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

1626 objects, are returned. 

1627 

1628 """ 

1629 

1630 return self._allrows() 

1631 

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

1633 return self._iter_impl() 

1634 

1635 def __next__(self) -> RowMapping: 

1636 return self._next_impl() 

1637 

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

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

1640 

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

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

1643 objects, are returned. 

1644 

1645 

1646 """ 

1647 return self._only_one_row( 

1648 raise_for_second_row=False, raise_for_none=False, scalar=False 

1649 ) 

1650 

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

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

1653 

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

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

1656 objects, are returned. 

1657 

1658 """ 

1659 return self._only_one_row( 

1660 raise_for_second_row=True, raise_for_none=False, scalar=False 

1661 ) 

1662 

1663 def one(self) -> RowMapping: 

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

1665 

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

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

1668 objects, are returned. 

1669 

1670 """ 

1671 return self._only_one_row( 

1672 raise_for_second_row=True, raise_for_none=True, scalar=False 

1673 ) 

1674 

1675 

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

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

1678 for caching. 

1679 

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

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

1682 object. 

1683 

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

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

1686 a callable:: 

1687 

1688 

1689 result = connection.execute(query) 

1690 

1691 frozen = result.freeze() 

1692 

1693 unfrozen_result_one = frozen() 

1694 

1695 for row in unfrozen_result_one: 

1696 print(row) 

1697 

1698 unfrozen_result_two = frozen() 

1699 rows = unfrozen_result_two.all() 

1700 

1701 # ... etc 

1702 

1703 .. versionadded:: 1.4 

1704 

1705 .. seealso:: 

1706 

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

1708 ORM to implement a result-set cache. 

1709 

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

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

1712 

1713 """ 

1714 

1715 data: Sequence[Any] 

1716 

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

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

1719 self._source_supports_scalars = result._source_supports_scalars 

1720 self._attributes = result._attributes 

1721 

1722 if self._source_supports_scalars: 

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

1724 else: 

1725 self.data = result.fetchall() 

1726 

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

1728 # used only by the orm fn merge_frozen_result 

1729 if self._source_supports_scalars: 

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

1731 else: 

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

1733 

1734 def with_new_rows( 

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

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

1737 fr = FrozenResult.__new__(FrozenResult) 

1738 fr.metadata = self.metadata 

1739 fr._attributes = self._attributes 

1740 fr._source_supports_scalars = self._source_supports_scalars 

1741 

1742 if self._source_supports_scalars: 

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

1744 else: 

1745 fr.data = tuple_data 

1746 return fr 

1747 

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

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

1750 self.metadata, iter(self.data) 

1751 ) 

1752 result._attributes = self._attributes 

1753 result._source_supports_scalars = self._source_supports_scalars 

1754 return result 

1755 

1756 

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

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

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

1760 

1761 .. versionadded:: 1.4 

1762 

1763 """ 

1764 

1765 _hard_closed = False 

1766 _soft_closed = False 

1767 

1768 def __init__( 

1769 self, 

1770 cursor_metadata: ResultMetaData, 

1771 iterator: Iterator[_InterimSupportsScalarsRowType], 

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

1773 _source_supports_scalars: bool = False, 

1774 ): 

1775 self._metadata = cursor_metadata 

1776 self.iterator = iterator 

1777 self.raw = raw 

1778 self._source_supports_scalars = _source_supports_scalars 

1779 

1780 @property 

1781 def closed(self) -> bool: 

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

1783 been closed 

1784 

1785 .. versionadded:: 1.4.43 

1786 

1787 """ 

1788 return self._hard_closed 

1789 

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

1791 if hard: 

1792 self._hard_closed = True 

1793 if self.raw is not None: 

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

1795 self.iterator = iter([]) 

1796 self._reset_memoizations() 

1797 self._soft_closed = True 

1798 

1799 def _raise_hard_closed(self) -> NoReturn: 

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

1801 

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

1803 return self.iterator 

1804 

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

1806 if self._hard_closed: 

1807 self._raise_hard_closed() 

1808 return self.iterator 

1809 

1810 def _fetchone_impl( 

1811 self, hard_close: bool = False 

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

1813 if self._hard_closed: 

1814 self._raise_hard_closed() 

1815 

1816 row = next(self.iterator, _NO_ROW) 

1817 if row is _NO_ROW: 

1818 self._soft_close(hard=hard_close) 

1819 return None 

1820 else: 

1821 return row 

1822 

1823 def _fetchall_impl( 

1824 self, 

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

1826 if self._hard_closed: 

1827 self._raise_hard_closed() 

1828 try: 

1829 return list(self.iterator) 

1830 finally: 

1831 self._soft_close() 

1832 

1833 def _fetchmany_impl( 

1834 self, size: Optional[int] = None 

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

1836 if self._hard_closed: 

1837 self._raise_hard_closed() 

1838 

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

1840 

1841 

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

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

1844 

1845 

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

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

1848 iterator-producing callable. 

1849 

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

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

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

1853 size. 

1854 

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

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

1857 

1858 .. versionadded:: 1.4 

1859 

1860 """ 

1861 

1862 def __init__( 

1863 self, 

1864 cursor_metadata: ResultMetaData, 

1865 chunks: Callable[ 

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

1867 ], 

1868 source_supports_scalars: bool = False, 

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

1870 dynamic_yield_per: bool = False, 

1871 ): 

1872 self._metadata = cursor_metadata 

1873 self.chunks = chunks 

1874 self._source_supports_scalars = source_supports_scalars 

1875 self.raw = raw 

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

1877 self.dynamic_yield_per = dynamic_yield_per 

1878 

1879 @_generative 

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

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

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

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

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

1885 # keep track. 

1886 

1887 self._yield_per = num 

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

1889 return self 

1890 

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

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

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

1894 

1895 def _fetchmany_impl( 

1896 self, size: Optional[int] = None 

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

1898 if self.dynamic_yield_per: 

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

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

1901 

1902 

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

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

1905 :class:`_engine.Result` objects. 

1906 

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

1908 

1909 .. versionadded:: 1.4 

1910 

1911 """ 

1912 

1913 closed = False 

1914 rowcount: Optional[int] 

1915 

1916 def __init__( 

1917 self, 

1918 cursor_metadata: ResultMetaData, 

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

1920 ): 

1921 self._results = results 

1922 super().__init__( 

1923 cursor_metadata, 

1924 itertools.chain.from_iterable( 

1925 r._raw_row_iterator() for r in results 

1926 ), 

1927 ) 

1928 

1929 self._unique_filter_state = results[0]._unique_filter_state 

1930 self._yield_per = results[0]._yield_per 

1931 

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

1933 self._source_supports_scalars = results[0]._source_supports_scalars 

1934 

1935 self._attributes = self._attributes.merge_with( 

1936 *[r._attributes for r in results] 

1937 ) 

1938 

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

1940 for r in self._results: 

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

1942 if hard: 

1943 self.closed = True