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

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

598 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 _create_unique_filters: Optional[ 

98 Callable[["Result[Any]"], Sequence[Optional[Callable[[Any], Any]]]] 

99 ] = None 

100 _keymap: _KeyMapType 

101 _keys: Sequence[str] 

102 _processors: Optional[_ProcessorsType] 

103 _key_to_index: Dict[_KeyType, int] 

104 

105 @property 

106 def keys(self) -> RMKeyView: 

107 return RMKeyView(self) 

108 

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

110 raise NotImplementedError() 

111 

112 def _for_freeze(self) -> ResultMetaData: 

113 raise NotImplementedError() 

114 

115 @overload 

116 def _key_fallback( 

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

118 ) -> NoReturn: ... 

119 

120 @overload 

121 def _key_fallback( 

122 self, 

123 key: Any, 

124 err: Optional[Exception], 

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

126 ) -> None: ... 

127 

128 @overload 

129 def _key_fallback( 

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

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

132 

133 def _key_fallback( 

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

135 ) -> Optional[NoReturn]: 

136 assert raiseerr 

137 raise KeyError(key) from err 

138 

139 def _raise_for_ambiguous_column_name( 

140 self, rec: _KeyMapRecType 

141 ) -> NoReturn: 

142 raise NotImplementedError( 

143 "ambiguous column name logic is implemented for " 

144 "CursorResultMetaData" 

145 ) 

146 

147 def _index_for_key( 

148 self, key: _KeyIndexType, raiseerr: bool 

149 ) -> Optional[int]: 

150 raise NotImplementedError() 

151 

152 def _indexes_for_keys( 

153 self, keys: Sequence[_KeyIndexType] 

154 ) -> Sequence[int]: 

155 raise NotImplementedError() 

156 

157 def _metadata_for_keys( 

158 self, keys: Sequence[_KeyIndexType] 

159 ) -> Iterator[_KeyMapRecType]: 

160 raise NotImplementedError() 

161 

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

163 raise NotImplementedError() 

164 

165 def _getter( 

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

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

168 index = self._index_for_key(key, raiseerr) 

169 

170 if index is not None: 

171 return operator.itemgetter(index) 

172 else: 

173 return None 

174 

175 def _row_as_tuple_getter( 

176 self, keys: Sequence[_KeyIndexType] 

177 ) -> _TupleGetterType: 

178 indexes = self._indexes_for_keys(keys) 

179 return tuplegetter(*indexes) 

180 

181 def _make_key_to_index( 

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

183 ) -> Dict[_KeyType, int]: 

184 return { 

185 key: rec[index] 

186 for key, rec in keymap.items() 

187 if rec[index] is not None 

188 } 

189 

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

191 if key in self._keymap: 

192 # the index must be none in this case 

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

194 else: 

195 # unknown key 

196 if attr_error: 

197 try: 

198 self._key_fallback(key, None) 

199 except KeyError as ke: 

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

201 else: 

202 self._key_fallback(key, None) 

203 

204 @property 

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

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

207 return None 

208 else: 

209 return self._processors 

210 

211 

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

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

214 

215 _parent: ResultMetaData 

216 _keys: Sequence[str] 

217 

218 def __init__(self, parent: ResultMetaData): 

219 self._parent = parent 

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

221 

222 def __len__(self) -> int: 

223 return len(self._keys) 

224 

225 def __repr__(self) -> str: 

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

227 

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

229 return iter(self._keys) 

230 

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

232 if isinstance(item, int): 

233 return False 

234 

235 # note this also includes special key fallback behaviors 

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

237 return self._parent._has_key(item) 

238 

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

240 return list(other) == list(self) 

241 

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

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

244 

245 

246class SimpleResultMetaData(ResultMetaData): 

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

248 

249 __slots__ = ( 

250 "_keys", 

251 "_keymap", 

252 "_processors", 

253 "_tuplefilter", 

254 "_translated_indexes", 

255 "_create_unique_filters", 

256 "_key_to_index", 

257 "_ambiguous_keys", 

258 ) 

259 

260 _keys: Sequence[str] 

261 

262 def __init__( 

263 self, 

264 keys: Sequence[str], 

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

266 _processors: Optional[_ProcessorsType] = None, 

267 _tuplefilter: Optional[_TupleGetterType] = None, 

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

269 _create_unique_filters: Optional[ 

270 Callable[ 

271 [Any], 

272 Sequence[Optional[Callable[[Any], Any]]], 

273 ] 

274 ] = None, 

275 _ambiguous_keys: Optional[frozenset[str]] = None, 

276 ): 

277 self._keys = list(keys) 

278 self._tuplefilter = _tuplefilter 

279 self._translated_indexes = _translated_indexes 

280 self._create_unique_filters = _create_unique_filters 

281 if extra: 

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

283 recs_names = [ 

284 ( 

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

286 (index, name, extras), 

287 ) 

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

289 ] 

290 else: 

291 recs_names = [ 

292 ((name,), (index, name, ())) 

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

294 ] 

295 

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

297 

298 if _ambiguous_keys: 

299 for name in _ambiguous_keys.intersection(self._keymap): 

300 rec = self._keymap[name] 

301 self._keymap[name] = (None,) + rec[1:] 

302 

303 self._processors = _processors 

304 

305 self._ambiguous_keys = _ambiguous_keys 

306 

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

308 

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

310 return key in self._keymap 

311 

312 def _for_freeze(self) -> ResultMetaData: 

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

314 # applied? 

315 create_unique_filters = self._create_unique_filters 

316 if create_unique_filters is not None and self._tuplefilter is not None: 

317 _tuplefilter = self._tuplefilter 

318 _orig_create_unique_filters = create_unique_filters 

319 

320 def create_unique_filters_filtered( 

321 result: Result[Any], 

322 ) -> Sequence[Optional[Callable[[Any], Any]]]: 

323 return _tuplefilter(_orig_create_unique_filters(result)) 

324 

325 create_unique_filters = create_unique_filters_filtered 

326 

327 return SimpleResultMetaData( 

328 self._keys, 

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

330 _create_unique_filters=create_unique_filters, 

331 _ambiguous_keys=self._ambiguous_keys, 

332 ) 

333 

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

335 return { 

336 "_keys": self._keys, 

337 "_translated_indexes": self._translated_indexes, 

338 "_ambiguous_keys": self._ambiguous_keys, 

339 } 

340 

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

342 if state["_translated_indexes"]: 

343 _translated_indexes = state["_translated_indexes"] 

344 _tuplefilter = tuplegetter(*_translated_indexes) 

345 else: 

346 _translated_indexes = _tuplefilter = None 

347 self.__init__( # type: ignore 

348 state["_keys"], 

349 _translated_indexes=_translated_indexes, 

350 _tuplefilter=_tuplefilter, 

351 _ambiguous_keys=state.get("_ambiguous_keys"), 

352 ) 

353 

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

355 if isinstance(key, int): 

356 key = self._keys[key] 

357 try: 

358 rec = self._keymap[key] 

359 except KeyError as ke: 

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

361 

362 if rec[0] is None: 

363 self._raise_for_ambiguous_column_name(rec) 

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

365 

366 def _raise_for_ambiguous_column_name( 

367 self, rec: _KeyMapRecType 

368 ) -> NoReturn: 

369 raise exc.InvalidRequestError( 

370 "Ambiguous column name '%s' in " 

371 "result set column descriptions" % rec[1] 

372 ) 

373 

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

375 # only used by the ORM with Column objects; does not need 

376 # ambiguous column name support 

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

378 

379 def _metadata_for_keys( 

380 self, keys: Sequence[Any] 

381 ) -> Iterator[_KeyMapRecType]: 

382 for key in keys: 

383 if isinstance(key, int): 

384 key = self._keys[key] 

385 

386 try: 

387 rec = self._keymap[key] 

388 except KeyError as ke: 

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

390 

391 if rec[0] is None: 

392 self._raise_for_ambiguous_column_name(rec) 

393 

394 yield rec 

395 

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

397 try: 

398 metadata_for_keys = [ 

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

400 for key in keys 

401 ] 

402 except KeyError as ke: 

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

404 

405 indexes: Sequence[int] 

406 new_keys: Sequence[str] 

407 extra: Sequence[Any] 

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

409 

410 if self._translated_indexes: 

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

412 

413 tup = tuplegetter(*indexes) 

414 

415 new_metadata = SimpleResultMetaData( 

416 new_keys, 

417 extra=extra, 

418 _tuplefilter=tup, 

419 _translated_indexes=indexes, 

420 _processors=self._processors, 

421 _create_unique_filters=self._create_unique_filters, 

422 ) 

423 

424 return new_metadata 

425 

426 

427def result_tuple( 

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

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

430 parent = SimpleResultMetaData(fields, extra) 

431 return functools.partial( 

432 Row, parent, parent._effective_processors, parent._key_to_index 

433 ) 

434 

435 

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

437 __slots__ = () 

438 _is_cursor = False 

439 

440 @_generative 

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

442 real_result = ( 

443 self._real_result 

444 if self._real_result 

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

446 ) 

447 

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

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

450 

451 assert self._generate_rows 

452 

453 return self 

454 

455 

456class _WithKeys: 

457 __slots__ = () 

458 

459 _metadata: ResultMetaData 

460 

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

462 def keys(self) -> RMKeyView: 

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

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

465 

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

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

468 execution. 

469 

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

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

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

473 

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

475 plain list. 

476 

477 

478 """ 

479 return self._metadata.keys 

480 

481 

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

483 """Represent a set of database results. 

484 

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

486 completely updated usage model and calling facade for SQLAlchemy 

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

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

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

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

491 is normally used. 

492 

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

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

495 yield instances of ORM mapped objects either individually or within 

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

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

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

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

500 method. 

501 

502 .. seealso:: 

503 

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

505 

506 """ 

507 

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

509 

510 _row_logging_fn: Optional[ 

511 Callable[[Row[Unpack[TupleAny]]], Row[Unpack[TupleAny]]] 

512 ] = None 

513 

514 _source_supports_scalars: bool = False 

515 

516 _yield_per: Optional[int] = None 

517 

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

519 

520 def __init__(self, cursor_metadata: ResultMetaData): 

521 self._metadata = cursor_metadata 

522 

523 def __enter__(self) -> Self: 

524 return self 

525 

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

527 self.close() 

528 

529 def close(self) -> None: 

530 """Hard close this :class:`_engine.Result`. 

531 

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

533 not implemented by default. The method should generally end 

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

535 subsequent iteration or row fetching to raise 

536 :class:`.ResourceClosedError`. 

537 

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

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

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

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

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

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

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

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

546 exhaust and autoclose the database cursor. 

547 

548 """ 

549 self._soft_close(hard=True) 

550 

551 @property 

552 def _soft_closed(self) -> bool: 

553 raise NotImplementedError() 

554 

555 @property 

556 def closed(self) -> bool: 

557 """Return ``True`` if this :class:`_engine.Result` was **hard closed** 

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

559 

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

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

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

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

564 

565 .. seealso:: 

566 

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

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

569 may return zero or more rows 

570 

571 """ 

572 raise NotImplementedError() 

573 

574 @_generative 

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

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

577 

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

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

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

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

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

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

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

585 rows remain if fewer remain. 

586 

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

588 conjunction with the 

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

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

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

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

593 

594 .. tip:: 

595 

596 Consider using the 

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

598 execution option, which will simultaneously set 

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

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

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

602 a fixed row buffer size at once. 

603 

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

605 execution option is available for ORM operations, with 

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

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

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

609 

610 .. versionadded:: 1.4 

611 

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

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

614 

615 .. seealso:: 

616 

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

618 :meth:`_engine.Result.yield_per` 

619 

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

621 

622 """ 

623 self._yield_per = num 

624 return self 

625 

626 @_generative 

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

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

629 :class:`_engine.Result`. 

630 

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

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

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

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

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

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

637 returned objects. 

638 

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

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

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

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

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

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

645 

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

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

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

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

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

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

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

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

654 of the requested size. 

655 

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

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

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

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

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

661 :class:`_engine.Result` object. 

662 

663 """ 

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

665 return self 

666 

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

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

669 

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

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

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

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

674 a given statement construct. 

675 

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

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

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

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

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

681 has been corrected such that calling upon 

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

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

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

685 only a single column. 

686 

687 E.g.:: 

688 

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

690 result = connection.execute(statement) 

691 

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

693 ... 

694 

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

696 

697 for z, y in result.columns( 

698 statement.selected_columns.c.z, statement.selected_columns.c.y 

699 ): 

700 ... 

701 

702 .. versionadded:: 1.4 

703 

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

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

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

707 

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

709 given. 

710 

711 """ 

712 return self._column_slices(col_expressions) 

713 

714 @overload 

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

716 

717 @overload 

718 def scalars( 

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

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

721 

722 @overload 

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

724 

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

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

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

728 

729 E.g.:: 

730 

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

732 >>> result.scalars().all() 

733 [1, 2, 3] 

734 

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

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

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

738 

739 .. versionadded:: 1.4 

740 

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

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

743 

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

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

746 

747 """ 

748 return ScalarResult(self, index) 

749 

750 def _getter( 

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

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

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

754 :class:`_engine.Row`. 

755 

756 """ 

757 if self._source_supports_scalars: 

758 raise NotImplementedError( 

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

760 ) 

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

762 

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

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

765 :class:`_engine.Row`. 

766 

767 """ 

768 if self._source_supports_scalars: 

769 raise NotImplementedError( 

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

771 ) 

772 return self._metadata._row_as_tuple_getter(keys) 

773 

774 def mappings(self) -> MappingResult: 

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

776 :class:`_engine.MappingResult`. 

777 

778 When this filter is applied, fetching rows will return 

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

780 objects. 

781 

782 .. versionadded:: 1.4 

783 

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

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

786 

787 """ 

788 

789 return MappingResult(self) 

790 

791 @property 

792 @deprecated( 

793 "2.1.0", 

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

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

796 ) 

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

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

799 

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

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

802 

803 .. versionadded:: 2.0 

804 

805 .. seealso:: 

806 

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

808 workaround for SQLAlchemy 2.1. 

809 

810 """ 

811 return self # type: ignore 

812 

813 @deprecated( 

814 "2.1.0", 

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

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

817 ) 

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

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

820 

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

822 at runtime, 

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

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

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

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

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

828 itself included typing information. 

829 

830 .. versionadded:: 2.0 

831 

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

833 

834 .. seealso:: 

835 

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

837 workaround for SQLAlchemy 2.1. 

838 

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

840 

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

842 

843 """ 

844 

845 return self # type: ignore 

846 

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

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

849 

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

851 to merge multiple compatible results together. 

852 

853 """ 

854 raise NotImplementedError() 

855 

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

857 return self._iter_impl() 

858 

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

860 return self._next_impl() 

861 

862 def partitions( 

863 self, size: Optional[int] = None 

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

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

866 

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

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

869 lists will be yielded. 

870 

871 The result object is automatically closed when the iterator 

872 is fully consumed. 

873 

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

875 ahead of time unless the 

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

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

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

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

880 

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

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

883 combined with use of the 

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

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

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

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

888 yielding them out. 

889 

890 .. versionadded:: 1.4 

891 

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

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

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

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

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

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

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

899 specific and not well defined. 

900 

901 :return: iterator of lists 

902 

903 .. seealso:: 

904 

905 :ref:`engine_stream_results` 

906 

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

908 

909 """ 

910 

911 getter = self._manyrow_getter 

912 

913 while True: 

914 partition = getter(self, size) 

915 if partition: 

916 yield partition 

917 else: 

918 break 

919 

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

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

922 

923 return self._allrows() 

924 

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

926 """Fetch one row. 

927 

928 When all rows are exhausted, returns None. 

929 

930 This method is provided for backwards compatibility with 

931 SQLAlchemy 1.x.x. 

932 

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

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

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

936 

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

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

939 

940 """ 

941 row = self._onerow_getter(self) 

942 if row is _NO_ROW: 

943 return None 

944 else: 

945 return row 

946 

947 def fetchmany( 

948 self, size: Optional[int] = None 

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

950 """Fetch many rows. 

951 

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

953 

954 This method is provided for backwards compatibility with 

955 SQLAlchemy 1.x.x. 

956 

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

958 method. 

959 

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

961 

962 .. seealso:: 

963 

964 :meth:`_engine.Result.partitions` 

965 

966 """ 

967 

968 return self._manyrow_getter(self, size) 

969 

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

971 """Return all rows in a sequence. 

972 

973 Closes the result set after invocation. Subsequent invocations 

974 will return an empty sequence. 

975 

976 .. versionadded:: 1.4 

977 

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

979 

980 .. seealso:: 

981 

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

983 without loading it completely in python. 

984 

985 """ 

986 

987 return self._allrows() 

988 

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

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

991 

992 Closes the result set and discards remaining rows. 

993 

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

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

996 column of the first row, use the 

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

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

999 :meth:`_engine.Result.first`. 

1000 

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

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

1003 SQL query which was invoked to produce this 

1004 :class:`_engine.Result`; 

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

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

1007 the first row will be discarded. 

1008 

1009 .. seealso:: 

1010 

1011 :ref:`migration_20_unify_select` 

1012 

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

1014 if no rows remain. 

1015 

1016 .. seealso:: 

1017 

1018 :meth:`_engine.Result.scalar` 

1019 

1020 :meth:`_engine.Result.one` 

1021 

1022 """ 

1023 

1024 return self._only_one_row( 

1025 raise_for_second_row=False, raise_for_none=False, scalar=False 

1026 ) 

1027 

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

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

1030 

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

1032 Raises :class:`.MultipleResultsFound` 

1033 if multiple rows are returned. 

1034 

1035 .. versionadded:: 1.4 

1036 

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

1038 is available. 

1039 

1040 :raises: :class:`.MultipleResultsFound` 

1041 

1042 .. seealso:: 

1043 

1044 :meth:`_engine.Result.first` 

1045 

1046 :meth:`_engine.Result.one` 

1047 

1048 """ 

1049 return self._only_one_row( 

1050 raise_for_second_row=True, raise_for_none=False, scalar=False 

1051 ) 

1052 

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

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

1055 

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

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

1058 

1059 .. seealso:: 

1060 

1061 :meth:`_engine.ScalarResult.one` 

1062 

1063 :meth:`_engine.Result.scalars` 

1064 

1065 """ 

1066 return self._only_one_row( 

1067 raise_for_second_row=True, raise_for_none=True, scalar=True 

1068 ) 

1069 

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

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

1072 

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

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

1075 

1076 .. seealso:: 

1077 

1078 :meth:`_engine.ScalarResult.one_or_none` 

1079 

1080 :meth:`_engine.Result.scalars` 

1081 

1082 """ 

1083 return self._only_one_row( 

1084 raise_for_second_row=True, raise_for_none=False, scalar=True 

1085 ) 

1086 

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

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

1089 

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

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

1092 would be returned. 

1093 

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

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

1096 column of the first row, use the 

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

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

1099 :meth:`_engine.Result.one`. 

1100 

1101 .. versionadded:: 1.4 

1102 

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

1104 

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

1106 

1107 .. seealso:: 

1108 

1109 :meth:`_engine.Result.first` 

1110 

1111 :meth:`_engine.Result.one_or_none` 

1112 

1113 :meth:`_engine.Result.scalar_one` 

1114 

1115 """ 

1116 return self._only_one_row( 

1117 raise_for_second_row=True, raise_for_none=True, scalar=False 

1118 ) 

1119 

1120 # special case to handle mypy issue: 

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

1122 @overload 

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

1124 pass 

1125 

1126 @overload 

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

1128 pass 

1129 

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

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

1132 

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

1134 

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

1136 

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

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

1139 method will have been called. 

1140 

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

1142 

1143 """ 

1144 return self._only_one_row( 

1145 raise_for_second_row=False, raise_for_none=False, scalar=True 

1146 ) 

1147 

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

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

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

1151 

1152 The callable object returned is an instance of 

1153 :class:`_engine.FrozenResult`. 

1154 

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

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

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

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

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

1160 against its stored set of rows. 

1161 

1162 .. seealso:: 

1163 

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

1165 ORM to implement a result-set cache. 

1166 

1167 """ 

1168 

1169 return FrozenResult(self) 

1170 

1171 def merge( 

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

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

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

1175 objects. 

1176 

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

1178 which will be composed of iterators from the given result 

1179 objects. 

1180 

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

1182 The subsequent result objects must be against an identical 

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

1184 undefined. 

1185 

1186 """ 

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

1188 

1189 

1190class FilterResult(ResultInternal[_R]): 

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

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

1193 

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

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

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

1197 

1198 """ 

1199 

1200 __slots__ = ( 

1201 "_real_result", 

1202 "_post_creational_filter", 

1203 "_metadata", 

1204 "_unique_filter_state", 

1205 "__dict__", 

1206 ) 

1207 

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

1209 

1210 _real_result: Result[Unpack[TupleAny]] 

1211 

1212 def __enter__(self) -> Self: 

1213 return self 

1214 

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

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

1217 

1218 @_generative 

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

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

1221 

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

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

1224 documentation for usage notes. 

1225 

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

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

1228 

1229 .. seealso:: 

1230 

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

1232 :meth:`_engine.Result.yield_per` 

1233 

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

1235 

1236 """ 

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

1238 return self 

1239 

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

1241 self._real_result._soft_close(hard=hard) 

1242 

1243 @property 

1244 def _soft_closed(self) -> bool: 

1245 return self._real_result._soft_closed 

1246 

1247 @property 

1248 def closed(self) -> bool: 

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

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

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

1252 method. 

1253 

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

1255 see that attribute for details. 

1256 

1257 """ 

1258 return self._real_result.closed 

1259 

1260 def close(self) -> None: 

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

1262 

1263 .. versionadded:: 1.4.43 

1264 

1265 """ 

1266 self._real_result.close() 

1267 

1268 @property 

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

1270 return self._real_result._attributes 

1271 

1272 def _fetchiter_impl( 

1273 self, 

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

1275 return self._real_result._fetchiter_impl() 

1276 

1277 def _fetchone_impl( 

1278 self, hard_close: bool = False 

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

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

1281 

1282 def _fetchall_impl( 

1283 self, 

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

1285 return self._real_result._fetchall_impl() 

1286 

1287 def _fetchmany_impl( 

1288 self, size: Optional[int] = None 

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

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

1291 

1292 

1293class ScalarResult(FilterResult[_R]): 

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

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

1296 

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

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

1299 

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

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

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

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

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

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

1306 

1307 """ 

1308 

1309 __slots__ = () 

1310 

1311 _generate_rows = False 

1312 

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

1314 

1315 def __init__( 

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

1317 ): 

1318 self._real_result = real_result 

1319 

1320 if real_result._source_supports_scalars: 

1321 self._metadata = real_result._metadata 

1322 self._post_creational_filter = None 

1323 else: 

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

1325 self._post_creational_filter = operator.itemgetter(0) 

1326 

1327 self._unique_filter_state = real_result._unique_filter_state 

1328 

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

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

1331 :class:`_engine.ScalarResult`. 

1332 

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

1334 

1335 """ 

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

1337 return self 

1338 

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

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

1341 

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

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

1344 are returned. 

1345 

1346 """ 

1347 

1348 getter = self._manyrow_getter 

1349 

1350 while True: 

1351 partition = getter(self, size) 

1352 if partition: 

1353 yield partition 

1354 else: 

1355 break 

1356 

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

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

1359 

1360 return self._allrows() 

1361 

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

1363 """Fetch many objects. 

1364 

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

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

1367 are returned. 

1368 

1369 """ 

1370 return self._manyrow_getter(self, size) 

1371 

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

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

1374 

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

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

1377 are returned. 

1378 

1379 """ 

1380 return self._allrows() 

1381 

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

1383 return self._iter_impl() 

1384 

1385 def __next__(self) -> _R: 

1386 return self._next_impl() 

1387 

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

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

1390 

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

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

1393 are returned. 

1394 

1395 

1396 """ 

1397 return self._only_one_row( 

1398 raise_for_second_row=False, raise_for_none=False, scalar=False 

1399 ) 

1400 

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

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

1403 

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

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

1406 are returned. 

1407 

1408 """ 

1409 return self._only_one_row( 

1410 raise_for_second_row=True, raise_for_none=False, scalar=False 

1411 ) 

1412 

1413 def one(self) -> _R: 

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

1415 

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

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

1418 are returned. 

1419 

1420 """ 

1421 return self._only_one_row( 

1422 raise_for_second_row=True, raise_for_none=True, scalar=False 

1423 ) 

1424 

1425 

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

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

1428 Python tuples instead of rows. 

1429 

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

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

1432 still used at runtime. 

1433 

1434 """ 

1435 

1436 __slots__ = () 

1437 

1438 if TYPE_CHECKING: 

1439 

1440 def partitions( 

1441 self, size: Optional[int] = None 

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

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

1444 

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

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

1447 are returned. 

1448 

1449 """ 

1450 ... 

1451 

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

1453 """Fetch one tuple. 

1454 

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

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

1457 objects, are returned. 

1458 

1459 """ 

1460 ... 

1461 

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

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

1464 ... 

1465 

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

1467 """Fetch many objects. 

1468 

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

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

1471 are returned. 

1472 

1473 """ 

1474 ... 

1475 

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

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

1478 

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

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

1481 are returned. 

1482 

1483 """ 

1484 ... 

1485 

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

1487 

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

1489 

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

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

1492 

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

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

1495 are returned. 

1496 

1497 

1498 """ 

1499 ... 

1500 

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

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

1503 

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

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

1506 are returned. 

1507 

1508 """ 

1509 ... 

1510 

1511 def one(self) -> _R: 

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

1513 

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

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

1516 are returned. 

1517 

1518 """ 

1519 ... 

1520 

1521 @overload 

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

1523 

1524 @overload 

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

1526 

1527 def scalar_one(self) -> Any: 

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

1529 

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

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

1532 

1533 .. seealso:: 

1534 

1535 :meth:`_engine.ScalarResult.one` 

1536 

1537 :meth:`_engine.Result.scalars` 

1538 

1539 """ 

1540 ... 

1541 

1542 @overload 

1543 def scalar_one_or_none( 

1544 self: TupleResult[Tuple[_T]], 

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

1546 

1547 @overload 

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

1549 

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

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

1552 

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

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

1555 

1556 .. seealso:: 

1557 

1558 :meth:`_engine.ScalarResult.one_or_none` 

1559 

1560 :meth:`_engine.Result.scalars` 

1561 

1562 """ 

1563 ... 

1564 

1565 @overload 

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

1567 

1568 @overload 

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

1570 

1571 def scalar(self) -> Any: 

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

1573 set. 

1574 

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

1576 

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

1578 

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

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

1581 method will have been called. 

1582 

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

1584 

1585 """ 

1586 ... 

1587 

1588 

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

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

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

1592 

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

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

1595 

1596 """ 

1597 

1598 __slots__ = () 

1599 

1600 _generate_rows = True 

1601 

1602 _post_creational_filter = operator.attrgetter("_mapping") 

1603 

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

1605 self._real_result = result 

1606 self._unique_filter_state = result._unique_filter_state 

1607 self._metadata = result._metadata 

1608 if result._source_supports_scalars: 

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

1610 

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

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

1613 :class:`_engine.MappingResult`. 

1614 

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

1616 

1617 """ 

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

1619 return self 

1620 

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

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

1623 return self._column_slices(col_expressions) 

1624 

1625 def partitions( 

1626 self, size: Optional[int] = None 

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

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

1629 

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

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

1632 objects, are returned. 

1633 

1634 """ 

1635 

1636 getter = self._manyrow_getter 

1637 

1638 while True: 

1639 partition = getter(self, size) 

1640 if partition: 

1641 yield partition 

1642 else: 

1643 break 

1644 

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

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

1647 

1648 return self._allrows() 

1649 

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

1651 """Fetch one object. 

1652 

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

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

1655 objects, are returned. 

1656 

1657 """ 

1658 

1659 row = self._onerow_getter(self) 

1660 if row is _NO_ROW: 

1661 return None 

1662 else: 

1663 return row 

1664 

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

1666 """Fetch many objects. 

1667 

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

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

1670 objects, are returned. 

1671 

1672 """ 

1673 

1674 return self._manyrow_getter(self, size) 

1675 

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

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

1678 

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

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

1681 objects, are returned. 

1682 

1683 """ 

1684 

1685 return self._allrows() 

1686 

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

1688 return self._iter_impl() 

1689 

1690 def __next__(self) -> RowMapping: 

1691 return self._next_impl() 

1692 

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

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

1695 

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

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

1698 objects, are returned. 

1699 

1700 

1701 """ 

1702 return self._only_one_row( 

1703 raise_for_second_row=False, raise_for_none=False, scalar=False 

1704 ) 

1705 

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

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

1708 

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

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

1711 objects, are returned. 

1712 

1713 """ 

1714 return self._only_one_row( 

1715 raise_for_second_row=True, raise_for_none=False, scalar=False 

1716 ) 

1717 

1718 def one(self) -> RowMapping: 

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

1720 

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

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

1723 objects, are returned. 

1724 

1725 """ 

1726 return self._only_one_row( 

1727 raise_for_second_row=True, raise_for_none=True, scalar=False 

1728 ) 

1729 

1730 

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

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

1733 for caching. 

1734 

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

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

1737 object. 

1738 

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

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

1741 a callable:: 

1742 

1743 

1744 result = connection.execute(query) 

1745 

1746 frozen = result.freeze() 

1747 

1748 unfrozen_result_one = frozen() 

1749 

1750 for row in unfrozen_result_one: 

1751 print(row) 

1752 

1753 unfrozen_result_two = frozen() 

1754 rows = unfrozen_result_two.all() 

1755 

1756 # ... etc 

1757 

1758 .. versionadded:: 1.4 

1759 

1760 .. seealso:: 

1761 

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

1763 ORM to implement a result-set cache. 

1764 

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

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

1767 

1768 """ 

1769 

1770 data: Sequence[Any] 

1771 

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

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

1774 self._source_supports_scalars = result._source_supports_scalars 

1775 self._attributes = result._attributes 

1776 

1777 if self._source_supports_scalars: 

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

1779 else: 

1780 self.data = result.fetchall() 

1781 

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

1783 # used only by the orm fn merge_frozen_result 

1784 if self._source_supports_scalars: 

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

1786 else: 

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

1788 

1789 def with_new_rows( 

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

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

1792 fr = FrozenResult.__new__(FrozenResult) 

1793 fr.metadata = self.metadata 

1794 fr._attributes = self._attributes 

1795 fr._source_supports_scalars = self._source_supports_scalars 

1796 

1797 if self._source_supports_scalars: 

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

1799 else: 

1800 fr.data = tuple_data 

1801 return fr 

1802 

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

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

1805 self.metadata, iter(self.data) 

1806 ) 

1807 result._attributes = self._attributes 

1808 result._source_supports_scalars = self._source_supports_scalars 

1809 return result 

1810 

1811 

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

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

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

1815 

1816 .. versionadded:: 1.4 

1817 

1818 """ 

1819 

1820 _hard_closed = False 

1821 _soft_closed = False 

1822 

1823 def __init__( 

1824 self, 

1825 cursor_metadata: ResultMetaData, 

1826 iterator: Iterator[_InterimSupportsScalarsRowType], 

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

1828 _source_supports_scalars: bool = False, 

1829 context: Optional[Any] = None, 

1830 ): 

1831 self._metadata = cursor_metadata 

1832 self.iterator = iterator 

1833 self.raw = raw 

1834 self._source_supports_scalars = _source_supports_scalars 

1835 self.context = context 

1836 

1837 @property 

1838 def closed(self) -> bool: 

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

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

1841 method. 

1842 

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

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

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

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

1847 

1848 .. seealso:: 

1849 

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

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

1852 may return zero or more rows 

1853 

1854 """ 

1855 return self._hard_closed 

1856 

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

1858 if hard: 

1859 self._hard_closed = True 

1860 if self.raw is not None: 

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

1862 self.iterator = iter([]) 

1863 self._reset_memoizations() 

1864 self._soft_closed = True 

1865 

1866 def _raise_hard_closed(self) -> NoReturn: 

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

1868 

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

1870 return self.iterator 

1871 

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

1873 if self._hard_closed: 

1874 self._raise_hard_closed() 

1875 return self.iterator 

1876 

1877 def _fetchone_impl( 

1878 self, hard_close: bool = False 

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

1880 if self._hard_closed: 

1881 self._raise_hard_closed() 

1882 

1883 row = next(self.iterator, _NO_ROW) 

1884 if row is _NO_ROW: 

1885 self._soft_close(hard=hard_close) 

1886 return None 

1887 else: 

1888 return row 

1889 

1890 def _fetchall_impl( 

1891 self, 

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

1893 if self._hard_closed: 

1894 self._raise_hard_closed() 

1895 try: 

1896 return list(self.iterator) 

1897 finally: 

1898 self._soft_close() 

1899 

1900 def _fetchmany_impl( 

1901 self, size: Optional[int] = None 

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

1903 if self._hard_closed: 

1904 self._raise_hard_closed() 

1905 

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

1907 

1908 

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

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

1911 

1912 

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

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

1915 iterator-producing callable. 

1916 

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

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

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

1920 size. 

1921 

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

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

1924 

1925 .. versionadded:: 1.4 

1926 

1927 """ 

1928 

1929 def __init__( 

1930 self, 

1931 cursor_metadata: ResultMetaData, 

1932 chunks: Callable[ 

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

1934 ], 

1935 source_supports_scalars: bool = False, 

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

1937 dynamic_yield_per: bool = False, 

1938 context: Optional[Any] = None, 

1939 ): 

1940 self._metadata = cursor_metadata 

1941 self.chunks = chunks 

1942 self._source_supports_scalars = source_supports_scalars 

1943 self.raw = raw 

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

1945 self.dynamic_yield_per = dynamic_yield_per 

1946 self.context = context 

1947 

1948 @_generative 

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

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

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

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

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

1954 # keep track. 

1955 

1956 self._yield_per = num 

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

1958 return self 

1959 

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

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

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

1963 

1964 def _fetchmany_impl( 

1965 self, size: Optional[int] = None 

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

1967 if self.dynamic_yield_per: 

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

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

1970 

1971 

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

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

1974 :class:`_engine.Result` objects. 

1975 

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

1977 

1978 .. versionadded:: 1.4 

1979 

1980 """ 

1981 

1982 closed = False 

1983 rowcount: Optional[int] 

1984 

1985 def __init__( 

1986 self, 

1987 cursor_metadata: ResultMetaData, 

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

1989 ): 

1990 self._results = results 

1991 super().__init__( 

1992 cursor_metadata, 

1993 itertools.chain.from_iterable( 

1994 r._raw_row_iterator() for r in results 

1995 ), 

1996 ) 

1997 

1998 self._unique_filter_state = results[0]._unique_filter_state 

1999 self._yield_per = results[0]._yield_per 

2000 

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

2002 self._source_supports_scalars = results[0]._source_supports_scalars 

2003 

2004 self._attributes = self._attributes.merge_with( 

2005 *[r._attributes for r in results] 

2006 ) 

2007 

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

2009 for r in self._results: 

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

2011 if hard: 

2012 self.closed = True