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 """Hard 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` was **hard closed** 

516 by explicitly calling the :meth:`close` method. 

517 

518 The attribute is **not** True if the :class:`_engine.Result` was only 

519 **soft closed**; a "soft close" is the style of close that takes place 

520 for example when the :class:`.CursorResult` is returned for a DML 

521 only statement without RETURNING, or when all result rows are fetched. 

522 

523 .. seealso:: 

524 

525 :attr:`.CursorResult.returns_rows` - attribute specific to 

526 :class:`.CursorResult` which indicates if the result is one that 

527 may return zero or more rows 

528 

529 """ 

530 raise NotImplementedError() 

531 

532 @_generative 

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

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

535 

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

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

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

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

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

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

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

543 rows remain if fewer remain. 

544 

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

546 conjunction with the 

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

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

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

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

551 

552 .. tip:: 

553 

554 Consider using the 

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

556 execution option, which will simultaneously set 

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

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

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

560 a fixed row buffer size at once. 

561 

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

563 execution option is available for ORM operations, with 

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

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

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

567 

568 .. versionadded:: 1.4 

569 

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

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

572 

573 .. seealso:: 

574 

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

576 :meth:`_engine.Result.yield_per` 

577 

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

579 

580 """ 

581 self._yield_per = num 

582 return self 

583 

584 @_generative 

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

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

587 :class:`_engine.Result`. 

588 

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

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

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

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

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

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

595 returned objects. 

596 

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

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

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

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

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

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

603 

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

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

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

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

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

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

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

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

612 of the requested size. 

613 

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

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

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

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

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

619 :class:`_engine.Result` object. 

620 

621 """ 

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

623 return self 

624 

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

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

627 

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

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

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

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

632 a given statement construct. 

633 

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

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

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

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

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

639 has been corrected such that calling upon 

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

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

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

643 only a single column. 

644 

645 E.g.:: 

646 

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

648 result = connection.execute(statement) 

649 

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

651 ... 

652 

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

654 

655 for z, y in result.columns( 

656 statement.selected_columns.c.z, statement.selected_columns.c.y 

657 ): 

658 ... 

659 

660 .. versionadded:: 1.4 

661 

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

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

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

665 

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

667 given. 

668 

669 """ 

670 return self._column_slices(col_expressions) 

671 

672 @overload 

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

674 

675 @overload 

676 def scalars( 

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

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

679 

680 @overload 

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

682 

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

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

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

686 

687 E.g.:: 

688 

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

690 >>> result.scalars().all() 

691 [1, 2, 3] 

692 

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

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

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

696 

697 .. versionadded:: 1.4 

698 

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

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

701 

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

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

704 

705 """ 

706 return ScalarResult(self, index) 

707 

708 def _getter( 

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

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

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

712 :class:`_engine.Row`. 

713 

714 """ 

715 if self._source_supports_scalars: 

716 raise NotImplementedError( 

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

718 ) 

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

720 

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

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

723 :class:`_engine.Row`. 

724 

725 """ 

726 if self._source_supports_scalars: 

727 raise NotImplementedError( 

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

729 ) 

730 return self._metadata._row_as_tuple_getter(keys) 

731 

732 def mappings(self) -> MappingResult: 

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

734 :class:`_engine.MappingResult`. 

735 

736 When this filter is applied, fetching rows will return 

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

738 objects. 

739 

740 .. versionadded:: 1.4 

741 

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

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

744 

745 """ 

746 

747 return MappingResult(self) 

748 

749 @property 

750 @deprecated( 

751 "2.1.0", 

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

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

754 ) 

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

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

757 

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

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

760 

761 .. versionadded:: 2.0 

762 

763 .. seealso:: 

764 

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

766 workaround for SQLAlchemy 2.1. 

767 

768 """ 

769 return self # type: ignore 

770 

771 @deprecated( 

772 "2.1.0", 

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

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

775 ) 

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

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

778 

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

780 at runtime, 

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

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

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

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

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

786 itself included typing information. 

787 

788 .. versionadded:: 2.0 

789 

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

791 

792 .. seealso:: 

793 

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

795 workaround for SQLAlchemy 2.1. 

796 

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

798 

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

800 

801 """ 

802 

803 return self # type: ignore 

804 

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

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

807 

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

809 to merge multiple compatible results together. 

810 

811 """ 

812 raise NotImplementedError() 

813 

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

815 return self._iter_impl() 

816 

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

818 return self._next_impl() 

819 

820 def partitions( 

821 self, size: Optional[int] = None 

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

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

824 

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

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

827 lists will be yielded. 

828 

829 The result object is automatically closed when the iterator 

830 is fully consumed. 

831 

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

833 ahead of time unless the 

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

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

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

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

838 

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

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

841 combined with use of the 

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

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

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

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

846 yielding them out. 

847 

848 .. versionadded:: 1.4 

849 

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

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

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

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

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

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

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

857 specific and not well defined. 

858 

859 :return: iterator of lists 

860 

861 .. seealso:: 

862 

863 :ref:`engine_stream_results` 

864 

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

866 

867 """ 

868 

869 getter = self._manyrow_getter 

870 

871 while True: 

872 partition = getter(self, size) 

873 if partition: 

874 yield partition 

875 else: 

876 break 

877 

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

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

880 

881 return self._allrows() 

882 

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

884 """Fetch one row. 

885 

886 When all rows are exhausted, returns None. 

887 

888 This method is provided for backwards compatibility with 

889 SQLAlchemy 1.x.x. 

890 

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

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

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

894 

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

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

897 

898 """ 

899 row = self._onerow_getter(self) 

900 if row is _NO_ROW: 

901 return None 

902 else: 

903 return row 

904 

905 def fetchmany( 

906 self, size: Optional[int] = None 

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

908 """Fetch many rows. 

909 

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

911 

912 This method is provided for backwards compatibility with 

913 SQLAlchemy 1.x.x. 

914 

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

916 method. 

917 

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

919 

920 .. seealso:: 

921 

922 :meth:`_engine.Result.partitions` 

923 

924 """ 

925 

926 return self._manyrow_getter(self, size) 

927 

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

929 """Return all rows in a sequence. 

930 

931 Closes the result set after invocation. Subsequent invocations 

932 will return an empty sequence. 

933 

934 .. versionadded:: 1.4 

935 

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

937 

938 .. seealso:: 

939 

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

941 without loading it completely in python. 

942 

943 """ 

944 

945 return self._allrows() 

946 

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

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

949 

950 Closes the result set and discards remaining rows. 

951 

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

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

954 column of the first row, use the 

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

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

957 :meth:`_engine.Result.first`. 

958 

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

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

961 SQL query which was invoked to produce this 

962 :class:`_engine.Result`; 

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

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

965 the first row will be discarded. 

966 

967 .. seealso:: 

968 

969 :ref:`migration_20_unify_select` 

970 

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

972 if no rows remain. 

973 

974 .. seealso:: 

975 

976 :meth:`_engine.Result.scalar` 

977 

978 :meth:`_engine.Result.one` 

979 

980 """ 

981 

982 return self._only_one_row( 

983 raise_for_second_row=False, raise_for_none=False, scalar=False 

984 ) 

985 

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

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

988 

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

990 Raises :class:`.MultipleResultsFound` 

991 if multiple rows are returned. 

992 

993 .. versionadded:: 1.4 

994 

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

996 is available. 

997 

998 :raises: :class:`.MultipleResultsFound` 

999 

1000 .. seealso:: 

1001 

1002 :meth:`_engine.Result.first` 

1003 

1004 :meth:`_engine.Result.one` 

1005 

1006 """ 

1007 return self._only_one_row( 

1008 raise_for_second_row=True, raise_for_none=False, scalar=False 

1009 ) 

1010 

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

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

1013 

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

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

1016 

1017 .. seealso:: 

1018 

1019 :meth:`_engine.ScalarResult.one` 

1020 

1021 :meth:`_engine.Result.scalars` 

1022 

1023 """ 

1024 return self._only_one_row( 

1025 raise_for_second_row=True, raise_for_none=True, scalar=True 

1026 ) 

1027 

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

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

1030 

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

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

1033 

1034 .. seealso:: 

1035 

1036 :meth:`_engine.ScalarResult.one_or_none` 

1037 

1038 :meth:`_engine.Result.scalars` 

1039 

1040 """ 

1041 return self._only_one_row( 

1042 raise_for_second_row=True, raise_for_none=False, scalar=True 

1043 ) 

1044 

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

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

1047 

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

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

1050 would be returned. 

1051 

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

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

1054 column of the first row, use the 

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

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

1057 :meth:`_engine.Result.one`. 

1058 

1059 .. versionadded:: 1.4 

1060 

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

1062 

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

1064 

1065 .. seealso:: 

1066 

1067 :meth:`_engine.Result.first` 

1068 

1069 :meth:`_engine.Result.one_or_none` 

1070 

1071 :meth:`_engine.Result.scalar_one` 

1072 

1073 """ 

1074 return self._only_one_row( 

1075 raise_for_second_row=True, raise_for_none=True, scalar=False 

1076 ) 

1077 

1078 # special case to handle mypy issue: 

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

1080 @overload 

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

1082 pass 

1083 

1084 @overload 

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

1086 pass 

1087 

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

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

1090 

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

1092 

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

1094 

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

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

1097 method will have been called. 

1098 

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

1100 

1101 """ 

1102 return self._only_one_row( 

1103 raise_for_second_row=False, raise_for_none=False, scalar=True 

1104 ) 

1105 

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

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

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

1109 

1110 The callable object returned is an instance of 

1111 :class:`_engine.FrozenResult`. 

1112 

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

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

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

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

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

1118 against its stored set of rows. 

1119 

1120 .. seealso:: 

1121 

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

1123 ORM to implement a result-set cache. 

1124 

1125 """ 

1126 

1127 return FrozenResult(self) 

1128 

1129 def merge( 

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

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

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

1133 objects. 

1134 

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

1136 which will be composed of iterators from the given result 

1137 objects. 

1138 

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

1140 The subsequent result objects must be against an identical 

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

1142 undefined. 

1143 

1144 """ 

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

1146 

1147 

1148class FilterResult(ResultInternal[_R]): 

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

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

1151 

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

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

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

1155 

1156 """ 

1157 

1158 __slots__ = ( 

1159 "_real_result", 

1160 "_post_creational_filter", 

1161 "_metadata", 

1162 "_unique_filter_state", 

1163 "__dict__", 

1164 ) 

1165 

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

1167 

1168 _real_result: Result[Unpack[TupleAny]] 

1169 

1170 def __enter__(self) -> Self: 

1171 return self 

1172 

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

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

1175 

1176 @_generative 

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

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

1179 

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

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

1182 documentation for usage notes. 

1183 

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

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

1186 

1187 .. seealso:: 

1188 

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

1190 :meth:`_engine.Result.yield_per` 

1191 

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

1193 

1194 """ 

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

1196 return self 

1197 

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

1199 self._real_result._soft_close(hard=hard) 

1200 

1201 @property 

1202 def _soft_closed(self) -> bool: 

1203 return self._real_result._soft_closed 

1204 

1205 @property 

1206 def closed(self) -> bool: 

1207 """Return ``True`` if this :class:`_engine.Result` being 

1208 proxied by this :class:`_engine.FilterResult` was 

1209 **hard closed** by explicitly calling the :meth:`_engine.Result.close` 

1210 method. 

1211 

1212 This is a direct proxy for the :attr:`_engine.Result.closed` attribute; 

1213 see that attribute for details. 

1214 

1215 """ 

1216 return self._real_result.closed 

1217 

1218 def close(self) -> None: 

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

1220 

1221 .. versionadded:: 1.4.43 

1222 

1223 """ 

1224 self._real_result.close() 

1225 

1226 @property 

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

1228 return self._real_result._attributes 

1229 

1230 def _fetchiter_impl( 

1231 self, 

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

1233 return self._real_result._fetchiter_impl() 

1234 

1235 def _fetchone_impl( 

1236 self, hard_close: bool = False 

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

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

1239 

1240 def _fetchall_impl( 

1241 self, 

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

1243 return self._real_result._fetchall_impl() 

1244 

1245 def _fetchmany_impl( 

1246 self, size: Optional[int] = None 

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

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

1249 

1250 

1251class ScalarResult(FilterResult[_R]): 

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

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

1254 

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

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

1257 

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

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

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

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

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

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

1264 

1265 """ 

1266 

1267 __slots__ = () 

1268 

1269 _generate_rows = False 

1270 

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

1272 

1273 def __init__( 

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

1275 ): 

1276 self._real_result = real_result 

1277 

1278 if real_result._source_supports_scalars: 

1279 self._metadata = real_result._metadata 

1280 self._post_creational_filter = None 

1281 else: 

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

1283 self._post_creational_filter = operator.itemgetter(0) 

1284 

1285 self._unique_filter_state = real_result._unique_filter_state 

1286 

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

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

1289 :class:`_engine.ScalarResult`. 

1290 

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

1292 

1293 """ 

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

1295 return self 

1296 

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

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

1299 

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

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

1302 are returned. 

1303 

1304 """ 

1305 

1306 getter = self._manyrow_getter 

1307 

1308 while True: 

1309 partition = getter(self, size) 

1310 if partition: 

1311 yield partition 

1312 else: 

1313 break 

1314 

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

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

1317 

1318 return self._allrows() 

1319 

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

1321 """Fetch many objects. 

1322 

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

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

1325 are returned. 

1326 

1327 """ 

1328 return self._manyrow_getter(self, size) 

1329 

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

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

1332 

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

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

1335 are returned. 

1336 

1337 """ 

1338 return self._allrows() 

1339 

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

1341 return self._iter_impl() 

1342 

1343 def __next__(self) -> _R: 

1344 return self._next_impl() 

1345 

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

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

1348 

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

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

1351 are returned. 

1352 

1353 

1354 """ 

1355 return self._only_one_row( 

1356 raise_for_second_row=False, raise_for_none=False, scalar=False 

1357 ) 

1358 

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

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

1361 

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

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

1364 are returned. 

1365 

1366 """ 

1367 return self._only_one_row( 

1368 raise_for_second_row=True, raise_for_none=False, scalar=False 

1369 ) 

1370 

1371 def one(self) -> _R: 

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

1373 

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

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

1376 are returned. 

1377 

1378 """ 

1379 return self._only_one_row( 

1380 raise_for_second_row=True, raise_for_none=True, scalar=False 

1381 ) 

1382 

1383 

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

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

1386 Python tuples instead of rows. 

1387 

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

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

1390 still used at runtime. 

1391 

1392 """ 

1393 

1394 __slots__ = () 

1395 

1396 if TYPE_CHECKING: 

1397 

1398 def partitions( 

1399 self, size: Optional[int] = None 

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

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

1402 

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

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

1405 are returned. 

1406 

1407 """ 

1408 ... 

1409 

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

1411 """Fetch one tuple. 

1412 

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

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

1415 objects, are returned. 

1416 

1417 """ 

1418 ... 

1419 

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

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

1422 ... 

1423 

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

1425 """Fetch many objects. 

1426 

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

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

1429 are returned. 

1430 

1431 """ 

1432 ... 

1433 

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

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

1436 

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

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

1439 are returned. 

1440 

1441 """ 

1442 ... 

1443 

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

1445 

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

1447 

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

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

1450 

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

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

1453 are returned. 

1454 

1455 

1456 """ 

1457 ... 

1458 

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

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

1461 

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

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

1464 are returned. 

1465 

1466 """ 

1467 ... 

1468 

1469 def one(self) -> _R: 

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

1471 

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

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

1474 are returned. 

1475 

1476 """ 

1477 ... 

1478 

1479 @overload 

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

1481 

1482 @overload 

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

1484 

1485 def scalar_one(self) -> Any: 

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

1487 

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

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

1490 

1491 .. seealso:: 

1492 

1493 :meth:`_engine.ScalarResult.one` 

1494 

1495 :meth:`_engine.Result.scalars` 

1496 

1497 """ 

1498 ... 

1499 

1500 @overload 

1501 def scalar_one_or_none( 

1502 self: TupleResult[Tuple[_T]], 

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

1504 

1505 @overload 

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

1507 

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

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

1510 

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

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

1513 

1514 .. seealso:: 

1515 

1516 :meth:`_engine.ScalarResult.one_or_none` 

1517 

1518 :meth:`_engine.Result.scalars` 

1519 

1520 """ 

1521 ... 

1522 

1523 @overload 

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

1525 

1526 @overload 

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

1528 

1529 def scalar(self) -> Any: 

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

1531 set. 

1532 

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

1534 

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

1536 

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

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

1539 method will have been called. 

1540 

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

1542 

1543 """ 

1544 ... 

1545 

1546 

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

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

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

1550 

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

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

1553 

1554 """ 

1555 

1556 __slots__ = () 

1557 

1558 _generate_rows = True 

1559 

1560 _post_creational_filter = operator.attrgetter("_mapping") 

1561 

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

1563 self._real_result = result 

1564 self._unique_filter_state = result._unique_filter_state 

1565 self._metadata = result._metadata 

1566 if result._source_supports_scalars: 

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

1568 

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

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

1571 :class:`_engine.MappingResult`. 

1572 

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

1574 

1575 """ 

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

1577 return self 

1578 

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

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

1581 return self._column_slices(col_expressions) 

1582 

1583 def partitions( 

1584 self, size: Optional[int] = None 

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

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

1587 

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

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

1590 objects, are returned. 

1591 

1592 """ 

1593 

1594 getter = self._manyrow_getter 

1595 

1596 while True: 

1597 partition = getter(self, size) 

1598 if partition: 

1599 yield partition 

1600 else: 

1601 break 

1602 

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

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

1605 

1606 return self._allrows() 

1607 

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

1609 """Fetch one object. 

1610 

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

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

1613 objects, are returned. 

1614 

1615 """ 

1616 

1617 row = self._onerow_getter(self) 

1618 if row is _NO_ROW: 

1619 return None 

1620 else: 

1621 return row 

1622 

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

1624 """Fetch many objects. 

1625 

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

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

1628 objects, are returned. 

1629 

1630 """ 

1631 

1632 return self._manyrow_getter(self, size) 

1633 

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

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

1636 

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

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

1639 objects, are returned. 

1640 

1641 """ 

1642 

1643 return self._allrows() 

1644 

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

1646 return self._iter_impl() 

1647 

1648 def __next__(self) -> RowMapping: 

1649 return self._next_impl() 

1650 

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

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

1653 

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

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

1656 objects, are returned. 

1657 

1658 

1659 """ 

1660 return self._only_one_row( 

1661 raise_for_second_row=False, raise_for_none=False, scalar=False 

1662 ) 

1663 

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

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

1666 

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

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

1669 objects, are returned. 

1670 

1671 """ 

1672 return self._only_one_row( 

1673 raise_for_second_row=True, raise_for_none=False, scalar=False 

1674 ) 

1675 

1676 def one(self) -> RowMapping: 

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

1678 

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

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

1681 objects, are returned. 

1682 

1683 """ 

1684 return self._only_one_row( 

1685 raise_for_second_row=True, raise_for_none=True, scalar=False 

1686 ) 

1687 

1688 

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

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

1691 for caching. 

1692 

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

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

1695 object. 

1696 

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

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

1699 a callable:: 

1700 

1701 

1702 result = connection.execute(query) 

1703 

1704 frozen = result.freeze() 

1705 

1706 unfrozen_result_one = frozen() 

1707 

1708 for row in unfrozen_result_one: 

1709 print(row) 

1710 

1711 unfrozen_result_two = frozen() 

1712 rows = unfrozen_result_two.all() 

1713 

1714 # ... etc 

1715 

1716 .. versionadded:: 1.4 

1717 

1718 .. seealso:: 

1719 

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

1721 ORM to implement a result-set cache. 

1722 

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

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

1725 

1726 """ 

1727 

1728 data: Sequence[Any] 

1729 

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

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

1732 self._source_supports_scalars = result._source_supports_scalars 

1733 self._attributes = result._attributes 

1734 

1735 if self._source_supports_scalars: 

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

1737 else: 

1738 self.data = result.fetchall() 

1739 

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

1741 # used only by the orm fn merge_frozen_result 

1742 if self._source_supports_scalars: 

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

1744 else: 

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

1746 

1747 def with_new_rows( 

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

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

1750 fr = FrozenResult.__new__(FrozenResult) 

1751 fr.metadata = self.metadata 

1752 fr._attributes = self._attributes 

1753 fr._source_supports_scalars = self._source_supports_scalars 

1754 

1755 if self._source_supports_scalars: 

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

1757 else: 

1758 fr.data = tuple_data 

1759 return fr 

1760 

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

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

1763 self.metadata, iter(self.data) 

1764 ) 

1765 result._attributes = self._attributes 

1766 result._source_supports_scalars = self._source_supports_scalars 

1767 return result 

1768 

1769 

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

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

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

1773 

1774 .. versionadded:: 1.4 

1775 

1776 """ 

1777 

1778 _hard_closed = False 

1779 _soft_closed = False 

1780 

1781 def __init__( 

1782 self, 

1783 cursor_metadata: ResultMetaData, 

1784 iterator: Iterator[_InterimSupportsScalarsRowType], 

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

1786 _source_supports_scalars: bool = False, 

1787 ): 

1788 self._metadata = cursor_metadata 

1789 self.iterator = iterator 

1790 self.raw = raw 

1791 self._source_supports_scalars = _source_supports_scalars 

1792 

1793 @property 

1794 def closed(self) -> bool: 

1795 """Return ``True`` if this :class:`_engine.IteratorResult` was 

1796 **hard closed** by explicitly calling the :meth:`_engine.Result.close` 

1797 method. 

1798 

1799 The attribute is **not** True if the :class:`_engine.Result` was only 

1800 **soft closed**; a "soft close" is the style of close that takes place 

1801 for example when the :class:`.CursorResult` is returned for a DML 

1802 only statement without RETURNING, or when all result rows are fetched. 

1803 

1804 .. seealso:: 

1805 

1806 :attr:`.CursorResult.returns_rows` - attribute specific to 

1807 :class:`.CursorResult` which indicates if the result is one that 

1808 may return zero or more rows 

1809 

1810 """ 

1811 return self._hard_closed 

1812 

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

1814 if hard: 

1815 self._hard_closed = True 

1816 if self.raw is not None: 

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

1818 self.iterator = iter([]) 

1819 self._reset_memoizations() 

1820 self._soft_closed = True 

1821 

1822 def _raise_hard_closed(self) -> NoReturn: 

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

1824 

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

1826 return self.iterator 

1827 

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

1829 if self._hard_closed: 

1830 self._raise_hard_closed() 

1831 return self.iterator 

1832 

1833 def _fetchone_impl( 

1834 self, hard_close: bool = False 

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

1836 if self._hard_closed: 

1837 self._raise_hard_closed() 

1838 

1839 row = next(self.iterator, _NO_ROW) 

1840 if row is _NO_ROW: 

1841 self._soft_close(hard=hard_close) 

1842 return None 

1843 else: 

1844 return row 

1845 

1846 def _fetchall_impl( 

1847 self, 

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

1849 if self._hard_closed: 

1850 self._raise_hard_closed() 

1851 try: 

1852 return list(self.iterator) 

1853 finally: 

1854 self._soft_close() 

1855 

1856 def _fetchmany_impl( 

1857 self, size: Optional[int] = None 

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

1859 if self._hard_closed: 

1860 self._raise_hard_closed() 

1861 

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

1863 

1864 

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

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

1867 

1868 

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

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

1871 iterator-producing callable. 

1872 

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

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

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

1876 size. 

1877 

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

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

1880 

1881 .. versionadded:: 1.4 

1882 

1883 """ 

1884 

1885 def __init__( 

1886 self, 

1887 cursor_metadata: ResultMetaData, 

1888 chunks: Callable[ 

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

1890 ], 

1891 source_supports_scalars: bool = False, 

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

1893 dynamic_yield_per: bool = False, 

1894 ): 

1895 self._metadata = cursor_metadata 

1896 self.chunks = chunks 

1897 self._source_supports_scalars = source_supports_scalars 

1898 self.raw = raw 

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

1900 self.dynamic_yield_per = dynamic_yield_per 

1901 

1902 @_generative 

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

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

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

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

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

1908 # keep track. 

1909 

1910 self._yield_per = num 

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

1912 return self 

1913 

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

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

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

1917 

1918 def _fetchmany_impl( 

1919 self, size: Optional[int] = None 

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

1921 if self.dynamic_yield_per: 

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

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

1924 

1925 

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

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

1928 :class:`_engine.Result` objects. 

1929 

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

1931 

1932 .. versionadded:: 1.4 

1933 

1934 """ 

1935 

1936 closed = False 

1937 rowcount: Optional[int] 

1938 

1939 def __init__( 

1940 self, 

1941 cursor_metadata: ResultMetaData, 

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

1943 ): 

1944 self._results = results 

1945 super().__init__( 

1946 cursor_metadata, 

1947 itertools.chain.from_iterable( 

1948 r._raw_row_iterator() for r in results 

1949 ), 

1950 ) 

1951 

1952 self._unique_filter_state = results[0]._unique_filter_state 

1953 self._yield_per = results[0]._yield_per 

1954 

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

1956 self._source_supports_scalars = results[0]._source_supports_scalars 

1957 

1958 self._attributes = self._attributes.merge_with( 

1959 *[r._attributes for r in results] 

1960 ) 

1961 

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

1963 for r in self._results: 

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

1965 if hard: 

1966 self.closed = True