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

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

833 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 

12from enum import Enum 

13import functools 

14import itertools 

15import operator 

16import typing 

17from typing import Any 

18from typing import Callable 

19from typing import cast 

20from typing import Dict 

21from typing import Generic 

22from typing import Iterable 

23from typing import Iterator 

24from typing import List 

25from typing import Mapping 

26from typing import NoReturn 

27from typing import Optional 

28from typing import overload 

29from typing import Sequence 

30from typing import Set 

31from typing import Tuple 

32from typing import TYPE_CHECKING 

33from typing import TypeVar 

34from typing import Union 

35 

36from .row import Row 

37from .row import RowMapping 

38from .. import exc 

39from .. import util 

40from ..sql.base import _generative 

41from ..sql.base import HasMemoized 

42from ..sql.base import InPlaceGenerative 

43from ..util import HasMemoized_ro_memoized_attribute 

44from ..util import NONE_SET 

45from ..util._has_cy import HAS_CYEXTENSION 

46from ..util.typing import Literal 

47from ..util.typing import Self 

48 

49if typing.TYPE_CHECKING or not HAS_CYEXTENSION: 

50 from ._py_row import tuplegetter as tuplegetter 

51else: 

52 from sqlalchemy.cyextension.resultproxy import tuplegetter as tuplegetter 

53 

54if typing.TYPE_CHECKING: 

55 from typing import Type 

56 

57 from .. import inspection 

58 from ..sql import roles 

59 from ..sql._typing import _HasClauseElement 

60 from ..sql.elements import SQLCoreOperations 

61 from ..sql.type_api import _ResultProcessorType 

62 

63_KeyType = Union[ 

64 str, 

65 "SQLCoreOperations[Any]", 

66 "roles.TypedColumnsClauseRole[Any]", 

67 "roles.ColumnsClauseRole", 

68 "Type[Any]", 

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

70] 

71_KeyIndexType = Union[_KeyType, int] 

72 

73# is overridden in cursor using _CursorKeyMapRecType 

74_KeyMapRecType = Any 

75 

76_KeyMapType = Mapping[_KeyType, _KeyMapRecType] 

77 

78 

79_RowData = Union[Row[Any], RowMapping, Any] 

80"""A generic form of "row" that accommodates for the different kinds of 

81"rows" that different result objects return, including row, row mapping, and 

82scalar values""" 

83 

84_RawRowType = Tuple[Any, ...] 

85"""represents the kind of row we get from a DBAPI cursor""" 

86 

87_R = TypeVar("_R", bound=_RowData) 

88_T = TypeVar("_T", bound=Any) 

89_TP = TypeVar("_TP", bound=Tuple[Any, ...]) 

90 

91_InterimRowType = Union[_R, _RawRowType] 

92"""a catchall "anything" kind of return type that can be applied 

93across all the result types 

94 

95""" 

96 

97_InterimSupportsScalarsRowType = Union[Row[Any], Any] 

98 

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

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

101_UniqueFilterType = Callable[[Any], Any] 

102_UniqueFilterStateType = Tuple[Set[Any], Optional[_UniqueFilterType]] 

103 

104 

105class ResultMetaData: 

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

107 

108 __slots__ = () 

109 

110 _tuplefilter: Optional[_TupleGetterType] = None 

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

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

113 _keymap: _KeyMapType 

114 _keys: Sequence[str] 

115 _processors: Optional[_ProcessorsType] 

116 _key_to_index: Mapping[_KeyType, int] 

117 

118 @property 

119 def keys(self) -> RMKeyView: 

120 return RMKeyView(self) 

121 

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

123 raise NotImplementedError() 

124 

125 def _for_freeze(self) -> ResultMetaData: 

126 raise NotImplementedError() 

127 

128 @overload 

129 def _key_fallback( 

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

131 ) -> NoReturn: ... 

132 

133 @overload 

134 def _key_fallback( 

135 self, 

136 key: Any, 

137 err: Optional[Exception], 

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

139 ) -> None: ... 

140 

141 @overload 

142 def _key_fallback( 

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

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

145 

146 def _key_fallback( 

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

148 ) -> Optional[NoReturn]: 

149 assert raiseerr 

150 raise KeyError(key) from err 

151 

152 def _raise_for_ambiguous_column_name( 

153 self, rec: _KeyMapRecType 

154 ) -> NoReturn: 

155 raise NotImplementedError( 

156 "ambiguous column name logic is implemented for " 

157 "CursorResultMetaData" 

158 ) 

159 

160 def _index_for_key( 

161 self, key: _KeyIndexType, raiseerr: bool 

162 ) -> Optional[int]: 

163 raise NotImplementedError() 

164 

165 def _indexes_for_keys( 

166 self, keys: Sequence[_KeyIndexType] 

167 ) -> Sequence[int]: 

168 raise NotImplementedError() 

169 

170 def _metadata_for_keys( 

171 self, keys: Sequence[_KeyIndexType] 

172 ) -> Iterator[_KeyMapRecType]: 

173 raise NotImplementedError() 

174 

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

176 raise NotImplementedError() 

177 

178 def _getter( 

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

180 ) -> Optional[Callable[[Row[Any]], Any]]: 

181 index = self._index_for_key(key, raiseerr) 

182 

183 if index is not None: 

184 return operator.itemgetter(index) 

185 else: 

186 return None 

187 

188 def _row_as_tuple_getter( 

189 self, keys: Sequence[_KeyIndexType] 

190 ) -> _TupleGetterType: 

191 indexes = self._indexes_for_keys(keys) 

192 return tuplegetter(*indexes) 

193 

194 def _make_key_to_index( 

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

196 ) -> Mapping[_KeyType, int]: 

197 return { 

198 key: rec[index] 

199 for key, rec in keymap.items() 

200 if rec[index] is not None 

201 } 

202 

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

204 if key in self._keymap: 

205 # the index must be none in this case 

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

207 else: 

208 # unknown key 

209 if attr_error: 

210 try: 

211 self._key_fallback(key, None) 

212 except KeyError as ke: 

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

214 else: 

215 self._key_fallback(key, None) 

216 

217 @property 

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

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

220 return None 

221 else: 

222 return self._processors 

223 

224 

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

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

227 

228 _parent: ResultMetaData 

229 _keys: Sequence[str] 

230 

231 def __init__(self, parent: ResultMetaData): 

232 self._parent = parent 

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

234 

235 def __len__(self) -> int: 

236 return len(self._keys) 

237 

238 def __repr__(self) -> str: 

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

240 

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

242 return iter(self._keys) 

243 

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

245 if isinstance(item, int): 

246 return False 

247 

248 # note this also includes special key fallback behaviors 

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

250 return self._parent._has_key(item) 

251 

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

253 return list(other) == list(self) 

254 

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

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

257 

258 

259class SimpleResultMetaData(ResultMetaData): 

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

261 

262 __slots__ = ( 

263 "_keys", 

264 "_keymap", 

265 "_processors", 

266 "_tuplefilter", 

267 "_translated_indexes", 

268 "_unique_filters", 

269 "_key_to_index", 

270 ) 

271 

272 _keys: Sequence[str] 

273 

274 def __init__( 

275 self, 

276 keys: Sequence[str], 

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

278 _processors: Optional[_ProcessorsType] = None, 

279 _tuplefilter: Optional[_TupleGetterType] = None, 

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

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

282 ): 

283 self._keys = list(keys) 

284 self._tuplefilter = _tuplefilter 

285 self._translated_indexes = _translated_indexes 

286 self._unique_filters = _unique_filters 

287 if extra: 

288 recs_names = [ 

289 ( 

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

291 (index, name, extras), 

292 ) 

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

294 ] 

295 else: 

296 recs_names = [ 

297 ((name,), (index, name, ())) 

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

299 ] 

300 

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

302 

303 self._processors = _processors 

304 

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

306 

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

308 return key in self._keymap 

309 

310 def _for_freeze(self) -> ResultMetaData: 

311 unique_filters = self._unique_filters 

312 if unique_filters and self._tuplefilter: 

313 unique_filters = self._tuplefilter(unique_filters) 

314 

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

316 # applied? 

317 return SimpleResultMetaData( 

318 self._keys, 

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

320 _unique_filters=unique_filters, 

321 ) 

322 

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

324 return { 

325 "_keys": self._keys, 

326 "_translated_indexes": self._translated_indexes, 

327 } 

328 

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

330 if state["_translated_indexes"]: 

331 _translated_indexes = state["_translated_indexes"] 

332 _tuplefilter = tuplegetter(*_translated_indexes) 

333 else: 

334 _translated_indexes = _tuplefilter = None 

335 self.__init__( # type: ignore 

336 state["_keys"], 

337 _translated_indexes=_translated_indexes, 

338 _tuplefilter=_tuplefilter, 

339 ) 

340 

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

342 if int in key.__class__.__mro__: 

343 key = self._keys[key] 

344 try: 

345 rec = self._keymap[key] 

346 except KeyError as ke: 

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

348 

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

350 

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

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

353 

354 def _metadata_for_keys( 

355 self, keys: Sequence[Any] 

356 ) -> Iterator[_KeyMapRecType]: 

357 for key in keys: 

358 if int in key.__class__.__mro__: 

359 key = self._keys[key] 

360 

361 try: 

362 rec = self._keymap[key] 

363 except KeyError as ke: 

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

365 

366 yield rec 

367 

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

369 try: 

370 metadata_for_keys = [ 

371 self._keymap[ 

372 self._keys[key] if int in key.__class__.__mro__ else key 

373 ] 

374 for key in keys 

375 ] 

376 except KeyError as ke: 

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

378 

379 indexes: Sequence[int] 

380 new_keys: Sequence[str] 

381 extra: Sequence[Any] 

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

383 

384 if self._translated_indexes: 

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

386 

387 tup = tuplegetter(*indexes) 

388 

389 new_metadata = SimpleResultMetaData( 

390 new_keys, 

391 extra=extra, 

392 _tuplefilter=tup, 

393 _translated_indexes=indexes, 

394 _processors=self._processors, 

395 _unique_filters=self._unique_filters, 

396 ) 

397 

398 return new_metadata 

399 

400 

401def result_tuple( 

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

403) -> Callable[[Iterable[Any]], Row[Any]]: 

404 parent = SimpleResultMetaData(fields, extra) 

405 return functools.partial( 

406 Row, parent, parent._effective_processors, parent._key_to_index 

407 ) 

408 

409 

410# a symbol that indicates to internal Result methods that 

411# "no row is returned". We can't use None for those cases where a scalar 

412# filter is applied to rows. 

413class _NoRow(Enum): 

414 _NO_ROW = 0 

415 

416 

417_NO_ROW = _NoRow._NO_ROW 

418 

419 

420class ResultInternal(InPlaceGenerative, Generic[_R]): 

421 __slots__ = () 

422 

423 _real_result: Optional[Result[Any]] = None 

424 _generate_rows: bool = True 

425 _row_logging_fn: Optional[Callable[[Any], Any]] 

426 

427 _unique_filter_state: Optional[_UniqueFilterStateType] = None 

428 _post_creational_filter: Optional[Callable[[Any], Any]] = None 

429 _is_cursor = False 

430 

431 _metadata: ResultMetaData 

432 

433 _source_supports_scalars: bool 

434 

435 def _fetchiter_impl(self) -> Iterator[_InterimRowType[Row[Any]]]: 

436 raise NotImplementedError() 

437 

438 def _fetchone_impl( 

439 self, hard_close: bool = False 

440 ) -> Optional[_InterimRowType[Row[Any]]]: 

441 raise NotImplementedError() 

442 

443 def _fetchmany_impl( 

444 self, size: Optional[int] = None 

445 ) -> List[_InterimRowType[Row[Any]]]: 

446 raise NotImplementedError() 

447 

448 def _fetchall_impl(self) -> List[_InterimRowType[Row[Any]]]: 

449 raise NotImplementedError() 

450 

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

452 raise NotImplementedError() 

453 

454 @HasMemoized_ro_memoized_attribute 

455 def _row_getter(self) -> Optional[Callable[..., _R]]: 

456 real_result: Result[Any] = ( 

457 self._real_result 

458 if self._real_result 

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

460 ) 

461 

462 if real_result._source_supports_scalars: 

463 if not self._generate_rows: 

464 return None 

465 else: 

466 _proc = Row 

467 

468 def process_row( 

469 metadata: ResultMetaData, 

470 processors: Optional[_ProcessorsType], 

471 key_to_index: Mapping[_KeyType, int], 

472 scalar_obj: Any, 

473 ) -> Row[Any]: 

474 return _proc( 

475 metadata, processors, key_to_index, (scalar_obj,) 

476 ) 

477 

478 else: 

479 process_row = Row # type: ignore 

480 

481 metadata = self._metadata 

482 

483 key_to_index = metadata._key_to_index 

484 processors = metadata._effective_processors 

485 tf = metadata._tuplefilter 

486 

487 if tf and not real_result._source_supports_scalars: 

488 if processors: 

489 processors = tf(processors) 

490 

491 _make_row_orig: Callable[..., _R] = functools.partial( # type: ignore # noqa E501 

492 process_row, metadata, processors, key_to_index 

493 ) 

494 

495 fixed_tf = tf 

496 

497 def make_row(row: _InterimRowType[Row[Any]]) -> _R: 

498 return _make_row_orig(fixed_tf(row)) 

499 

500 else: 

501 make_row = functools.partial( # type: ignore 

502 process_row, metadata, processors, key_to_index 

503 ) 

504 

505 if real_result._row_logging_fn: 

506 _log_row = real_result._row_logging_fn 

507 _make_row = make_row 

508 

509 def make_row(row: _InterimRowType[Row[Any]]) -> _R: 

510 return _log_row(_make_row(row)) # type: ignore 

511 

512 return make_row 

513 

514 @HasMemoized_ro_memoized_attribute 

515 def _iterator_getter(self) -> Callable[..., Iterator[_R]]: 

516 make_row = self._row_getter 

517 

518 post_creational_filter = self._post_creational_filter 

519 

520 if self._unique_filter_state: 

521 uniques, strategy = self._unique_strategy 

522 

523 def iterrows(self: Result[Any]) -> Iterator[_R]: 

524 for raw_row in self._fetchiter_impl(): 

525 obj: _InterimRowType[Any] = ( 

526 make_row(raw_row) if make_row else raw_row 

527 ) 

528 hashed = strategy(obj) if strategy else obj 

529 if hashed in uniques: 

530 continue 

531 uniques.add(hashed) 

532 if post_creational_filter: 

533 obj = post_creational_filter(obj) 

534 yield obj # type: ignore 

535 

536 else: 

537 

538 def iterrows(self: Result[Any]) -> Iterator[_R]: 

539 for raw_row in self._fetchiter_impl(): 

540 row: _InterimRowType[Any] = ( 

541 make_row(raw_row) if make_row else raw_row 

542 ) 

543 if post_creational_filter: 

544 row = post_creational_filter(row) 

545 yield row # type: ignore 

546 

547 return iterrows 

548 

549 def _raw_all_rows(self) -> List[_R]: 

550 make_row = self._row_getter 

551 assert make_row is not None 

552 rows = self._fetchall_impl() 

553 return [make_row(row) for row in rows] 

554 

555 def _allrows(self) -> List[_R]: 

556 post_creational_filter = self._post_creational_filter 

557 

558 make_row = self._row_getter 

559 

560 rows = self._fetchall_impl() 

561 made_rows: List[_InterimRowType[_R]] 

562 if make_row: 

563 made_rows = [make_row(row) for row in rows] 

564 else: 

565 made_rows = rows # type: ignore 

566 

567 interim_rows: List[_R] 

568 

569 if self._unique_filter_state: 

570 uniques, strategy = self._unique_strategy 

571 

572 interim_rows = [ 

573 made_row # type: ignore 

574 for made_row, sig_row in [ 

575 ( 

576 made_row, 

577 strategy(made_row) if strategy else made_row, 

578 ) 

579 for made_row in made_rows 

580 ] 

581 if sig_row not in uniques and not uniques.add(sig_row) # type: ignore # noqa: E501 

582 ] 

583 else: 

584 interim_rows = made_rows # type: ignore 

585 

586 if post_creational_filter: 

587 interim_rows = [ 

588 post_creational_filter(row) for row in interim_rows 

589 ] 

590 return interim_rows 

591 

592 @HasMemoized_ro_memoized_attribute 

593 def _onerow_getter( 

594 self, 

595 ) -> Callable[..., Union[Literal[_NoRow._NO_ROW], _R]]: 

596 make_row = self._row_getter 

597 

598 post_creational_filter = self._post_creational_filter 

599 

600 if self._unique_filter_state: 

601 uniques, strategy = self._unique_strategy 

602 

603 def onerow(self: Result[Any]) -> Union[_NoRow, _R]: 

604 _onerow = self._fetchone_impl 

605 while True: 

606 row = _onerow() 

607 if row is None: 

608 return _NO_ROW 

609 else: 

610 obj: _InterimRowType[Any] = ( 

611 make_row(row) if make_row else row 

612 ) 

613 hashed = strategy(obj) if strategy else obj 

614 if hashed in uniques: 

615 continue 

616 else: 

617 uniques.add(hashed) 

618 if post_creational_filter: 

619 obj = post_creational_filter(obj) 

620 return obj # type: ignore 

621 

622 else: 

623 

624 def onerow(self: Result[Any]) -> Union[_NoRow, _R]: 

625 row = self._fetchone_impl() 

626 if row is None: 

627 return _NO_ROW 

628 else: 

629 interim_row: _InterimRowType[Any] = ( 

630 make_row(row) if make_row else row 

631 ) 

632 if post_creational_filter: 

633 interim_row = post_creational_filter(interim_row) 

634 return interim_row # type: ignore 

635 

636 return onerow 

637 

638 @HasMemoized_ro_memoized_attribute 

639 def _manyrow_getter(self) -> Callable[..., List[_R]]: 

640 make_row = self._row_getter 

641 

642 post_creational_filter = self._post_creational_filter 

643 

644 if self._unique_filter_state: 

645 uniques, strategy = self._unique_strategy 

646 

647 def filterrows( 

648 make_row: Optional[Callable[..., _R]], 

649 rows: List[Any], 

650 strategy: Optional[Callable[[List[Any]], Any]], 

651 uniques: Set[Any], 

652 ) -> List[_R]: 

653 if make_row: 

654 rows = [make_row(row) for row in rows] 

655 

656 if strategy: 

657 made_rows = ( 

658 (made_row, strategy(made_row)) for made_row in rows 

659 ) 

660 else: 

661 made_rows = ((made_row, made_row) for made_row in rows) 

662 return [ 

663 made_row 

664 for made_row, sig_row in made_rows 

665 if sig_row not in uniques and not uniques.add(sig_row) # type: ignore # noqa: E501 

666 ] 

667 

668 def manyrows( 

669 self: ResultInternal[_R], num: Optional[int] 

670 ) -> List[_R]: 

671 collect: List[_R] = [] 

672 

673 _manyrows = self._fetchmany_impl 

674 

675 if num is None: 

676 # if None is passed, we don't know the default 

677 # manyrows number, DBAPI has this as cursor.arraysize 

678 # different DBAPIs / fetch strategies may be different. 

679 # do a fetch to find what the number is. if there are 

680 # only fewer rows left, then it doesn't matter. 

681 real_result = ( 

682 self._real_result 

683 if self._real_result 

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

685 ) 

686 if real_result._yield_per: 

687 num_required = num = real_result._yield_per 

688 else: 

689 rows = _manyrows(num) 

690 num = len(rows) 

691 assert make_row is not None 

692 collect.extend( 

693 filterrows(make_row, rows, strategy, uniques) 

694 ) 

695 num_required = num - len(collect) 

696 else: 

697 num_required = num 

698 

699 assert num is not None 

700 

701 while num_required: 

702 rows = _manyrows(num_required) 

703 if not rows: 

704 break 

705 

706 collect.extend( 

707 filterrows(make_row, rows, strategy, uniques) 

708 ) 

709 num_required = num - len(collect) 

710 

711 if post_creational_filter: 

712 collect = [post_creational_filter(row) for row in collect] 

713 return collect 

714 

715 else: 

716 

717 def manyrows( 

718 self: ResultInternal[_R], num: Optional[int] 

719 ) -> List[_R]: 

720 if num is None: 

721 real_result = ( 

722 self._real_result 

723 if self._real_result 

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

725 ) 

726 num = real_result._yield_per 

727 

728 rows: List[_InterimRowType[Any]] = self._fetchmany_impl(num) 

729 if make_row: 

730 rows = [make_row(row) for row in rows] 

731 if post_creational_filter: 

732 rows = [post_creational_filter(row) for row in rows] 

733 return rows # type: ignore 

734 

735 return manyrows 

736 

737 @overload 

738 def _only_one_row( 

739 self: ResultInternal[Row[Any]], 

740 raise_for_second_row: bool, 

741 raise_for_none: bool, 

742 scalar: Literal[True], 

743 ) -> Any: ... 

744 

745 @overload 

746 def _only_one_row( 

747 self, 

748 raise_for_second_row: bool, 

749 raise_for_none: Literal[True], 

750 scalar: bool, 

751 ) -> _R: ... 

752 

753 @overload 

754 def _only_one_row( 

755 self, 

756 raise_for_second_row: bool, 

757 raise_for_none: bool, 

758 scalar: bool, 

759 ) -> Optional[_R]: ... 

760 

761 def _only_one_row( 

762 self, 

763 raise_for_second_row: bool, 

764 raise_for_none: bool, 

765 scalar: bool, 

766 ) -> Optional[_R]: 

767 onerow = self._fetchone_impl 

768 

769 row: Optional[_InterimRowType[Any]] = onerow(hard_close=True) 

770 if row is None: 

771 if raise_for_none: 

772 raise exc.NoResultFound( 

773 "No row was found when one was required" 

774 ) 

775 else: 

776 return None 

777 

778 if scalar and self._source_supports_scalars: 

779 self._generate_rows = False 

780 make_row = None 

781 else: 

782 make_row = self._row_getter 

783 

784 try: 

785 row = make_row(row) if make_row else row 

786 except: 

787 self._soft_close(hard=True) 

788 raise 

789 

790 if raise_for_second_row: 

791 if self._unique_filter_state: 

792 # for no second row but uniqueness, need to essentially 

793 # consume the entire result :( 

794 uniques, strategy = self._unique_strategy 

795 

796 existing_row_hash = strategy(row) if strategy else row 

797 

798 while True: 

799 next_row: Any = onerow(hard_close=True) 

800 if next_row is None: 

801 next_row = _NO_ROW 

802 break 

803 

804 try: 

805 next_row = make_row(next_row) if make_row else next_row 

806 

807 if strategy: 

808 assert next_row is not _NO_ROW 

809 if existing_row_hash == strategy(next_row): 

810 continue 

811 elif row == next_row: 

812 continue 

813 # here, we have a row and it's different 

814 break 

815 except: 

816 self._soft_close(hard=True) 

817 raise 

818 else: 

819 next_row = onerow(hard_close=True) 

820 if next_row is None: 

821 next_row = _NO_ROW 

822 

823 if next_row is not _NO_ROW: 

824 self._soft_close(hard=True) 

825 raise exc.MultipleResultsFound( 

826 "Multiple rows were found when exactly one was required" 

827 if raise_for_none 

828 else "Multiple rows were found when one or none " 

829 "was required" 

830 ) 

831 else: 

832 # if we checked for second row then that would have 

833 # closed us :) 

834 self._soft_close(hard=True) 

835 

836 if not scalar: 

837 post_creational_filter = self._post_creational_filter 

838 if post_creational_filter: 

839 row = post_creational_filter(row) 

840 

841 if scalar and make_row: 

842 return row[0] # type: ignore 

843 else: 

844 return row # type: ignore 

845 

846 def _iter_impl(self) -> Iterator[_R]: 

847 return self._iterator_getter(self) 

848 

849 def _next_impl(self) -> _R: 

850 row = self._onerow_getter(self) 

851 if row is _NO_ROW: 

852 raise StopIteration() 

853 else: 

854 return row 

855 

856 @_generative 

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

858 real_result = ( 

859 self._real_result 

860 if self._real_result 

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

862 ) 

863 

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

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

866 

867 assert self._generate_rows 

868 

869 return self 

870 

871 @HasMemoized.memoized_attribute 

872 def _unique_strategy(self) -> _UniqueFilterStateType: 

873 assert self._unique_filter_state is not None 

874 uniques, strategy = self._unique_filter_state 

875 

876 real_result = ( 

877 self._real_result 

878 if self._real_result is not None 

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

880 ) 

881 

882 if not strategy and self._metadata._unique_filters: 

883 if ( 

884 real_result._source_supports_scalars 

885 and not self._generate_rows 

886 ): 

887 strategy = self._metadata._unique_filters[0] 

888 else: 

889 filters = self._metadata._unique_filters 

890 if self._metadata._tuplefilter: 

891 filters = self._metadata._tuplefilter(filters) 

892 

893 strategy = operator.methodcaller("_filter_on_values", filters) 

894 return uniques, strategy 

895 

896 

897class _WithKeys: 

898 __slots__ = () 

899 

900 _metadata: ResultMetaData 

901 

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

903 def keys(self) -> RMKeyView: 

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

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

906 

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

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

909 execution. 

910 

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

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

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

914 

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

916 plain list. 

917 

918 

919 """ 

920 return self._metadata.keys 

921 

922 

923class Result(_WithKeys, ResultInternal[Row[_TP]]): 

924 """Represent a set of database results. 

925 

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

927 completely updated usage model and calling facade for SQLAlchemy 

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

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

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

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

932 is normally used. 

933 

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

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

936 yield instances of ORM mapped objects either individually or within 

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

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

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

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

941 method. 

942 

943 .. seealso:: 

944 

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

946 

947 """ 

948 

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

950 

951 _row_logging_fn: Optional[Callable[[Row[Any]], Row[Any]]] = None 

952 

953 _source_supports_scalars: bool = False 

954 

955 _yield_per: Optional[int] = None 

956 

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

958 

959 def __init__(self, cursor_metadata: ResultMetaData): 

960 self._metadata = cursor_metadata 

961 

962 def __enter__(self) -> Self: 

963 return self 

964 

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

966 self.close() 

967 

968 def close(self) -> None: 

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

970 

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

972 not implemented by default. The method should generally end 

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

974 subsequent iteration or row fetching to raise 

975 :class:`.ResourceClosedError`. 

976 

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

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

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

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

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

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

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

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

985 exhaust and autoclose the database cursor. 

986 

987 """ 

988 self._soft_close(hard=True) 

989 

990 @property 

991 def _soft_closed(self) -> bool: 

992 raise NotImplementedError() 

993 

994 @property 

995 def closed(self) -> bool: 

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

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

998 

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

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

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

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

1003 

1004 .. seealso:: 

1005 

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

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

1008 may return zero or more rows 

1009 

1010 """ 

1011 raise NotImplementedError() 

1012 

1013 @_generative 

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

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

1016 

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

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

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

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

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

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

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

1024 rows remain if fewer remain. 

1025 

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

1027 conjunction with the 

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

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

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

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

1032 

1033 .. tip:: 

1034 

1035 Consider using the 

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

1037 execution option, which will simultaneously set 

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

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

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

1041 a fixed row buffer size at once. 

1042 

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

1044 execution option is available for ORM operations, with 

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

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

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

1048 

1049 .. versionadded:: 1.4 

1050 

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

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

1053 

1054 .. seealso:: 

1055 

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

1057 :meth:`_engine.Result.yield_per` 

1058 

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

1060 

1061 """ 

1062 self._yield_per = num 

1063 return self 

1064 

1065 @_generative 

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

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

1068 :class:`_engine.Result`. 

1069 

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

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

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

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

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

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

1076 returned objects. 

1077 

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

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

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

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

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

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

1084 

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

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

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

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

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

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

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

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

1093 of the requested size. 

1094 

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

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

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

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

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

1100 :class:`_engine.Result` object. 

1101 

1102 """ 

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

1104 return self 

1105 

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

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

1108 

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

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

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

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

1113 a given statement construct. 

1114 

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

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

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

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

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

1120 has been corrected such that calling upon 

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

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

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

1124 only a single column. 

1125 

1126 E.g.:: 

1127 

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

1129 result = connection.execute(statement) 

1130 

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

1132 ... 

1133 

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

1135 

1136 for z, y in result.columns( 

1137 statement.selected_columns.c.z, statement.selected_columns.c.y 

1138 ): 

1139 ... 

1140 

1141 .. versionadded:: 1.4 

1142 

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

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

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

1146 

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

1148 given. 

1149 

1150 """ 

1151 return self._column_slices(col_expressions) 

1152 

1153 @overload 

1154 def scalars(self: Result[Tuple[_T]]) -> ScalarResult[_T]: ... 

1155 

1156 @overload 

1157 def scalars( 

1158 self: Result[Tuple[_T]], index: Literal[0] 

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

1160 

1161 @overload 

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

1163 

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

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

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

1167 

1168 E.g.:: 

1169 

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

1171 >>> result.scalars().all() 

1172 [1, 2, 3] 

1173 

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

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

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

1177 

1178 .. versionadded:: 1.4 

1179 

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

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

1182 

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

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

1185 

1186 """ 

1187 return ScalarResult(self, index) 

1188 

1189 def _getter( 

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

1191 ) -> Optional[Callable[[Row[Any]], Any]]: 

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

1193 :class:`_engine.Row`. 

1194 

1195 """ 

1196 if self._source_supports_scalars: 

1197 raise NotImplementedError( 

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

1199 ) 

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

1201 

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

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

1204 :class:`_engine.Row`. 

1205 

1206 """ 

1207 if self._source_supports_scalars: 

1208 raise NotImplementedError( 

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

1210 ) 

1211 return self._metadata._row_as_tuple_getter(keys) 

1212 

1213 def mappings(self) -> MappingResult: 

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

1215 :class:`_engine.MappingResult`. 

1216 

1217 When this filter is applied, fetching rows will return 

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

1219 objects. 

1220 

1221 .. versionadded:: 1.4 

1222 

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

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

1225 

1226 """ 

1227 

1228 return MappingResult(self) 

1229 

1230 @property 

1231 def t(self) -> TupleResult[_TP]: 

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

1233 

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

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

1236 

1237 .. versionadded:: 2.0 

1238 

1239 """ 

1240 return self # type: ignore 

1241 

1242 def tuples(self) -> TupleResult[_TP]: 

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

1244 

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

1246 at runtime, 

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

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

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

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

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

1252 itself included typing information. 

1253 

1254 .. versionadded:: 2.0 

1255 

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

1257 

1258 .. seealso:: 

1259 

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

1261 

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

1263 

1264 """ 

1265 

1266 return self # type: ignore 

1267 

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

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

1270 

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

1272 to merge multiple compatible results together. 

1273 

1274 """ 

1275 raise NotImplementedError() 

1276 

1277 def __iter__(self) -> Iterator[Row[_TP]]: 

1278 return self._iter_impl() 

1279 

1280 def __next__(self) -> Row[_TP]: 

1281 return self._next_impl() 

1282 

1283 def partitions( 

1284 self, size: Optional[int] = None 

1285 ) -> Iterator[Sequence[Row[_TP]]]: 

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

1287 

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

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

1290 lists will be yielded. 

1291 

1292 The result object is automatically closed when the iterator 

1293 is fully consumed. 

1294 

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

1296 ahead of time unless the 

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

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

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

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

1301 

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

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

1304 combined with use of the 

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

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

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

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

1309 yielding them out. 

1310 

1311 .. versionadded:: 1.4 

1312 

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

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

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

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

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

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

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

1320 specific and not well defined. 

1321 

1322 :return: iterator of lists 

1323 

1324 .. seealso:: 

1325 

1326 :ref:`engine_stream_results` 

1327 

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

1329 

1330 """ 

1331 

1332 getter = self._manyrow_getter 

1333 

1334 while True: 

1335 partition = getter(self, size) 

1336 if partition: 

1337 yield partition 

1338 else: 

1339 break 

1340 

1341 def fetchall(self) -> Sequence[Row[_TP]]: 

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

1343 

1344 return self._allrows() 

1345 

1346 def fetchone(self) -> Optional[Row[_TP]]: 

1347 """Fetch one row. 

1348 

1349 When all rows are exhausted, returns None. 

1350 

1351 This method is provided for backwards compatibility with 

1352 SQLAlchemy 1.x.x. 

1353 

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

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

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

1357 

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

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

1360 

1361 """ 

1362 row = self._onerow_getter(self) 

1363 if row is _NO_ROW: 

1364 return None 

1365 else: 

1366 return row 

1367 

1368 def fetchmany(self, size: Optional[int] = None) -> Sequence[Row[_TP]]: 

1369 """Fetch many rows. 

1370 

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

1372 

1373 This method is provided for backwards compatibility with 

1374 SQLAlchemy 1.x.x. 

1375 

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

1377 method. 

1378 

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

1380 

1381 .. seealso:: 

1382 

1383 :meth:`_engine.Result.partitions` 

1384 

1385 """ 

1386 

1387 return self._manyrow_getter(self, size) 

1388 

1389 def all(self) -> Sequence[Row[_TP]]: 

1390 """Return all rows in a sequence. 

1391 

1392 Closes the result set after invocation. Subsequent invocations 

1393 will return an empty sequence. 

1394 

1395 .. versionadded:: 1.4 

1396 

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

1398 

1399 .. seealso:: 

1400 

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

1402 without loading it completely in python. 

1403 

1404 """ 

1405 

1406 return self._allrows() 

1407 

1408 def first(self) -> Optional[Row[_TP]]: 

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

1410 

1411 Closes the result set and discards remaining rows. 

1412 

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

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

1415 column of the first row, use the 

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

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

1418 :meth:`_engine.Result.first`. 

1419 

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

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

1422 SQL query which was invoked to produce this 

1423 :class:`_engine.Result`; 

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

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

1426 the first row will be discarded. 

1427 

1428 .. seealso:: 

1429 

1430 :ref:`migration_20_unify_select` 

1431 

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

1433 if no rows remain. 

1434 

1435 .. seealso:: 

1436 

1437 :meth:`_engine.Result.scalar` 

1438 

1439 :meth:`_engine.Result.one` 

1440 

1441 """ 

1442 

1443 return self._only_one_row( 

1444 raise_for_second_row=False, raise_for_none=False, scalar=False 

1445 ) 

1446 

1447 def one_or_none(self) -> Optional[Row[_TP]]: 

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

1449 

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

1451 Raises :class:`.MultipleResultsFound` 

1452 if multiple rows are returned. 

1453 

1454 .. versionadded:: 1.4 

1455 

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

1457 is available. 

1458 

1459 :raises: :class:`.MultipleResultsFound` 

1460 

1461 .. seealso:: 

1462 

1463 :meth:`_engine.Result.first` 

1464 

1465 :meth:`_engine.Result.one` 

1466 

1467 """ 

1468 return self._only_one_row( 

1469 raise_for_second_row=True, raise_for_none=False, scalar=False 

1470 ) 

1471 

1472 @overload 

1473 def scalar_one(self: Result[Tuple[_T]]) -> _T: ... 

1474 

1475 @overload 

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

1477 

1478 def scalar_one(self) -> Any: 

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

1480 

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

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

1483 

1484 .. seealso:: 

1485 

1486 :meth:`_engine.ScalarResult.one` 

1487 

1488 :meth:`_engine.Result.scalars` 

1489 

1490 """ 

1491 return self._only_one_row( 

1492 raise_for_second_row=True, raise_for_none=True, scalar=True 

1493 ) 

1494 

1495 @overload 

1496 def scalar_one_or_none(self: Result[Tuple[_T]]) -> Optional[_T]: ... 

1497 

1498 @overload 

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

1500 

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

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

1503 

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

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

1506 

1507 .. seealso:: 

1508 

1509 :meth:`_engine.ScalarResult.one_or_none` 

1510 

1511 :meth:`_engine.Result.scalars` 

1512 

1513 """ 

1514 return self._only_one_row( 

1515 raise_for_second_row=True, raise_for_none=False, scalar=True 

1516 ) 

1517 

1518 def one(self) -> Row[_TP]: 

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

1520 

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

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

1523 would be returned. 

1524 

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

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

1527 column of the first row, use the 

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

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

1530 :meth:`_engine.Result.one`. 

1531 

1532 .. versionadded:: 1.4 

1533 

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

1535 

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

1537 

1538 .. seealso:: 

1539 

1540 :meth:`_engine.Result.first` 

1541 

1542 :meth:`_engine.Result.one_or_none` 

1543 

1544 :meth:`_engine.Result.scalar_one` 

1545 

1546 """ 

1547 return self._only_one_row( 

1548 raise_for_second_row=True, raise_for_none=True, scalar=False 

1549 ) 

1550 

1551 @overload 

1552 def scalar(self: Result[Tuple[_T]]) -> Optional[_T]: ... 

1553 

1554 @overload 

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

1556 

1557 def scalar(self) -> Any: 

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

1559 

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

1561 

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

1563 

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

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

1566 method will have been called. 

1567 

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

1569 

1570 """ 

1571 return self._only_one_row( 

1572 raise_for_second_row=False, raise_for_none=False, scalar=True 

1573 ) 

1574 

1575 def freeze(self) -> FrozenResult[_TP]: 

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

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

1578 

1579 The callable object returned is an instance of 

1580 :class:`_engine.FrozenResult`. 

1581 

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

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

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

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

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

1587 against its stored set of rows. 

1588 

1589 .. seealso:: 

1590 

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

1592 ORM to implement a result-set cache. 

1593 

1594 """ 

1595 

1596 return FrozenResult(self) 

1597 

1598 def merge(self, *others: Result[Any]) -> MergedResult[_TP]: 

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

1600 objects. 

1601 

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

1603 which will be composed of iterators from the given result 

1604 objects. 

1605 

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

1607 The subsequent result objects must be against an identical 

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

1609 undefined. 

1610 

1611 """ 

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

1613 

1614 

1615class FilterResult(ResultInternal[_R]): 

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

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

1618 

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

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

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

1622 

1623 """ 

1624 

1625 __slots__ = ( 

1626 "_real_result", 

1627 "_post_creational_filter", 

1628 "_metadata", 

1629 "_unique_filter_state", 

1630 "__dict__", 

1631 ) 

1632 

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

1634 

1635 _real_result: Result[Any] 

1636 

1637 def __enter__(self) -> Self: 

1638 return self 

1639 

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

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

1642 

1643 @_generative 

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

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

1646 

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

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

1649 documentation for usage notes. 

1650 

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

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

1653 

1654 .. seealso:: 

1655 

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

1657 :meth:`_engine.Result.yield_per` 

1658 

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

1660 

1661 """ 

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

1663 return self 

1664 

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

1666 self._real_result._soft_close(hard=hard) 

1667 

1668 @property 

1669 def _soft_closed(self) -> bool: 

1670 return self._real_result._soft_closed 

1671 

1672 @property 

1673 def closed(self) -> bool: 

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

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

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

1677 method. 

1678 

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

1680 see that attribute for details. 

1681 

1682 """ 

1683 return self._real_result.closed 

1684 

1685 def close(self) -> None: 

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

1687 

1688 .. versionadded:: 1.4.43 

1689 

1690 """ 

1691 self._real_result.close() 

1692 

1693 @property 

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

1695 return self._real_result._attributes 

1696 

1697 def _fetchiter_impl(self) -> Iterator[_InterimRowType[Row[Any]]]: 

1698 return self._real_result._fetchiter_impl() 

1699 

1700 def _fetchone_impl( 

1701 self, hard_close: bool = False 

1702 ) -> Optional[_InterimRowType[Row[Any]]]: 

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

1704 

1705 def _fetchall_impl(self) -> List[_InterimRowType[Row[Any]]]: 

1706 return self._real_result._fetchall_impl() 

1707 

1708 def _fetchmany_impl( 

1709 self, size: Optional[int] = None 

1710 ) -> List[_InterimRowType[Row[Any]]]: 

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

1712 

1713 

1714class ScalarResult(FilterResult[_R]): 

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

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

1717 

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

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

1720 

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

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

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

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

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

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

1727 

1728 """ 

1729 

1730 __slots__ = () 

1731 

1732 _generate_rows = False 

1733 

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

1735 

1736 def __init__(self, real_result: Result[Any], index: _KeyIndexType): 

1737 self._real_result = real_result 

1738 

1739 if real_result._source_supports_scalars: 

1740 self._metadata = real_result._metadata 

1741 self._post_creational_filter = None 

1742 else: 

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

1744 self._post_creational_filter = operator.itemgetter(0) 

1745 

1746 self._unique_filter_state = real_result._unique_filter_state 

1747 

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

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

1750 :class:`_engine.ScalarResult`. 

1751 

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

1753 

1754 """ 

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

1756 return self 

1757 

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

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

1760 

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

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

1763 are returned. 

1764 

1765 """ 

1766 

1767 getter = self._manyrow_getter 

1768 

1769 while True: 

1770 partition = getter(self, size) 

1771 if partition: 

1772 yield partition 

1773 else: 

1774 break 

1775 

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

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

1778 

1779 return self._allrows() 

1780 

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

1782 """Fetch many objects. 

1783 

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

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

1786 are returned. 

1787 

1788 """ 

1789 return self._manyrow_getter(self, size) 

1790 

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

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

1793 

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

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

1796 are returned. 

1797 

1798 """ 

1799 return self._allrows() 

1800 

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

1802 return self._iter_impl() 

1803 

1804 def __next__(self) -> _R: 

1805 return self._next_impl() 

1806 

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

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

1809 

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

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

1812 are returned. 

1813 

1814 

1815 """ 

1816 return self._only_one_row( 

1817 raise_for_second_row=False, raise_for_none=False, scalar=False 

1818 ) 

1819 

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

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

1822 

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

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

1825 are returned. 

1826 

1827 """ 

1828 return self._only_one_row( 

1829 raise_for_second_row=True, raise_for_none=False, scalar=False 

1830 ) 

1831 

1832 def one(self) -> _R: 

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

1834 

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

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

1837 are returned. 

1838 

1839 """ 

1840 return self._only_one_row( 

1841 raise_for_second_row=True, raise_for_none=True, scalar=False 

1842 ) 

1843 

1844 

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

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

1847 Python tuples instead of rows. 

1848 

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

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

1851 still used at runtime. 

1852 

1853 """ 

1854 

1855 __slots__ = () 

1856 

1857 if TYPE_CHECKING: 

1858 

1859 def partitions( 

1860 self, size: Optional[int] = None 

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

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

1863 

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

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

1866 are returned. 

1867 

1868 """ 

1869 ... 

1870 

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

1872 """Fetch one tuple. 

1873 

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

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

1876 objects, are returned. 

1877 

1878 """ 

1879 ... 

1880 

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

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

1883 ... 

1884 

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

1886 """Fetch many objects. 

1887 

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

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

1890 are returned. 

1891 

1892 """ 

1893 ... 

1894 

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

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

1897 

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

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

1900 are returned. 

1901 

1902 """ 

1903 ... 

1904 

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

1906 

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

1908 

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

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

1911 

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

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

1914 are returned. 

1915 

1916 

1917 """ 

1918 ... 

1919 

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

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

1922 

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

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

1925 are returned. 

1926 

1927 """ 

1928 ... 

1929 

1930 def one(self) -> _R: 

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

1932 

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

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

1935 are returned. 

1936 

1937 """ 

1938 ... 

1939 

1940 @overload 

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

1942 

1943 @overload 

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

1945 

1946 def scalar_one(self) -> Any: 

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

1948 

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

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

1951 

1952 .. seealso:: 

1953 

1954 :meth:`_engine.ScalarResult.one` 

1955 

1956 :meth:`_engine.Result.scalars` 

1957 

1958 """ 

1959 ... 

1960 

1961 @overload 

1962 def scalar_one_or_none( 

1963 self: TupleResult[Tuple[_T]], 

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

1965 

1966 @overload 

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

1968 

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

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

1971 

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

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

1974 

1975 .. seealso:: 

1976 

1977 :meth:`_engine.ScalarResult.one_or_none` 

1978 

1979 :meth:`_engine.Result.scalars` 

1980 

1981 """ 

1982 ... 

1983 

1984 @overload 

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

1986 

1987 @overload 

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

1989 

1990 def scalar(self) -> Any: 

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

1992 set. 

1993 

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

1995 

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

1997 

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

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

2000 method will have been called. 

2001 

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

2003 

2004 """ 

2005 ... 

2006 

2007 

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

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

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

2011 

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

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

2014 

2015 """ 

2016 

2017 __slots__ = () 

2018 

2019 _generate_rows = True 

2020 

2021 _post_creational_filter = operator.attrgetter("_mapping") 

2022 

2023 def __init__(self, result: Result[Any]): 

2024 self._real_result = result 

2025 self._unique_filter_state = result._unique_filter_state 

2026 self._metadata = result._metadata 

2027 if result._source_supports_scalars: 

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

2029 

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

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

2032 :class:`_engine.MappingResult`. 

2033 

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

2035 

2036 """ 

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

2038 return self 

2039 

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

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

2042 return self._column_slices(col_expressions) 

2043 

2044 def partitions( 

2045 self, size: Optional[int] = None 

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

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

2048 

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

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

2051 objects, are returned. 

2052 

2053 """ 

2054 

2055 getter = self._manyrow_getter 

2056 

2057 while True: 

2058 partition = getter(self, size) 

2059 if partition: 

2060 yield partition 

2061 else: 

2062 break 

2063 

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

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

2066 

2067 return self._allrows() 

2068 

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

2070 """Fetch one object. 

2071 

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

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

2074 objects, are returned. 

2075 

2076 """ 

2077 

2078 row = self._onerow_getter(self) 

2079 if row is _NO_ROW: 

2080 return None 

2081 else: 

2082 return row 

2083 

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

2085 """Fetch many objects. 

2086 

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

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

2089 objects, are returned. 

2090 

2091 """ 

2092 

2093 return self._manyrow_getter(self, size) 

2094 

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

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

2097 

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

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

2100 objects, are returned. 

2101 

2102 """ 

2103 

2104 return self._allrows() 

2105 

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

2107 return self._iter_impl() 

2108 

2109 def __next__(self) -> RowMapping: 

2110 return self._next_impl() 

2111 

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

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

2114 

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

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

2117 objects, are returned. 

2118 

2119 

2120 """ 

2121 return self._only_one_row( 

2122 raise_for_second_row=False, raise_for_none=False, scalar=False 

2123 ) 

2124 

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

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

2127 

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

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

2130 objects, are returned. 

2131 

2132 """ 

2133 return self._only_one_row( 

2134 raise_for_second_row=True, raise_for_none=False, scalar=False 

2135 ) 

2136 

2137 def one(self) -> RowMapping: 

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

2139 

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

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

2142 objects, are returned. 

2143 

2144 """ 

2145 return self._only_one_row( 

2146 raise_for_second_row=True, raise_for_none=True, scalar=False 

2147 ) 

2148 

2149 

2150class FrozenResult(Generic[_TP]): 

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

2152 for caching. 

2153 

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

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

2156 object. 

2157 

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

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

2160 a callable:: 

2161 

2162 

2163 result = connection.execute(query) 

2164 

2165 frozen = result.freeze() 

2166 

2167 unfrozen_result_one = frozen() 

2168 

2169 for row in unfrozen_result_one: 

2170 print(row) 

2171 

2172 unfrozen_result_two = frozen() 

2173 rows = unfrozen_result_two.all() 

2174 

2175 # ... etc 

2176 

2177 .. versionadded:: 1.4 

2178 

2179 .. seealso:: 

2180 

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

2182 ORM to implement a result-set cache. 

2183 

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

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

2186 

2187 """ 

2188 

2189 data: Sequence[Any] 

2190 

2191 def __init__(self, result: Result[_TP]): 

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

2193 self._source_supports_scalars = result._source_supports_scalars 

2194 self._attributes = result._attributes 

2195 

2196 if self._source_supports_scalars: 

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

2198 else: 

2199 self.data = result.fetchall() 

2200 

2201 def rewrite_rows(self) -> Sequence[Sequence[Any]]: 

2202 if self._source_supports_scalars: 

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

2204 else: 

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

2206 

2207 def with_new_rows( 

2208 self, tuple_data: Sequence[Row[_TP]] 

2209 ) -> FrozenResult[_TP]: 

2210 fr = FrozenResult.__new__(FrozenResult) 

2211 fr.metadata = self.metadata 

2212 fr._attributes = self._attributes 

2213 fr._source_supports_scalars = self._source_supports_scalars 

2214 

2215 if self._source_supports_scalars: 

2216 fr.data = [d[0] for d in tuple_data] 

2217 else: 

2218 fr.data = tuple_data 

2219 return fr 

2220 

2221 def __call__(self) -> Result[_TP]: 

2222 result: IteratorResult[_TP] = IteratorResult( 

2223 self.metadata, iter(self.data) 

2224 ) 

2225 result._attributes = self._attributes 

2226 result._source_supports_scalars = self._source_supports_scalars 

2227 return result 

2228 

2229 

2230class IteratorResult(Result[_TP]): 

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

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

2233 

2234 .. versionadded:: 1.4 

2235 

2236 """ 

2237 

2238 _hard_closed = False 

2239 _soft_closed = False 

2240 

2241 def __init__( 

2242 self, 

2243 cursor_metadata: ResultMetaData, 

2244 iterator: Iterator[_InterimSupportsScalarsRowType], 

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

2246 _source_supports_scalars: bool = False, 

2247 ): 

2248 self._metadata = cursor_metadata 

2249 self.iterator = iterator 

2250 self.raw = raw 

2251 self._source_supports_scalars = _source_supports_scalars 

2252 

2253 @property 

2254 def closed(self) -> bool: 

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

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

2257 method. 

2258 

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

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

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

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

2263 

2264 .. seealso:: 

2265 

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

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

2268 may return zero or more rows 

2269 

2270 """ 

2271 return self._hard_closed 

2272 

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

2274 if hard: 

2275 self._hard_closed = True 

2276 if self.raw is not None: 

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

2278 self.iterator = iter([]) 

2279 self._reset_memoizations() 

2280 self._soft_closed = True 

2281 

2282 def _raise_hard_closed(self) -> NoReturn: 

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

2284 

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

2286 return self.iterator 

2287 

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

2289 if self._hard_closed: 

2290 self._raise_hard_closed() 

2291 return self.iterator 

2292 

2293 def _fetchone_impl( 

2294 self, hard_close: bool = False 

2295 ) -> Optional[_InterimRowType[Row[Any]]]: 

2296 if self._hard_closed: 

2297 self._raise_hard_closed() 

2298 

2299 row = next(self.iterator, _NO_ROW) 

2300 if row is _NO_ROW: 

2301 self._soft_close(hard=hard_close) 

2302 return None 

2303 else: 

2304 return row 

2305 

2306 def _fetchall_impl(self) -> List[_InterimRowType[Row[Any]]]: 

2307 if self._hard_closed: 

2308 self._raise_hard_closed() 

2309 try: 

2310 return list(self.iterator) 

2311 finally: 

2312 self._soft_close() 

2313 

2314 def _fetchmany_impl( 

2315 self, size: Optional[int] = None 

2316 ) -> List[_InterimRowType[Row[Any]]]: 

2317 if self._hard_closed: 

2318 self._raise_hard_closed() 

2319 

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

2321 

2322 

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

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

2325 

2326 

2327class ChunkedIteratorResult(IteratorResult[_TP]): 

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

2329 iterator-producing callable. 

2330 

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

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

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

2334 size. 

2335 

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

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

2338 

2339 .. versionadded:: 1.4 

2340 

2341 """ 

2342 

2343 def __init__( 

2344 self, 

2345 cursor_metadata: ResultMetaData, 

2346 chunks: Callable[ 

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

2348 ], 

2349 source_supports_scalars: bool = False, 

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

2351 dynamic_yield_per: bool = False, 

2352 ): 

2353 self._metadata = cursor_metadata 

2354 self.chunks = chunks 

2355 self._source_supports_scalars = source_supports_scalars 

2356 self.raw = raw 

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

2358 self.dynamic_yield_per = dynamic_yield_per 

2359 

2360 @_generative 

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

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

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

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

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

2366 # keep track. 

2367 

2368 self._yield_per = num 

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

2370 return self 

2371 

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

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

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

2375 

2376 def _fetchmany_impl( 

2377 self, size: Optional[int] = None 

2378 ) -> List[_InterimRowType[Row[Any]]]: 

2379 if self.dynamic_yield_per: 

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

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

2382 

2383 

2384class MergedResult(IteratorResult[_TP]): 

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

2386 :class:`_engine.Result` objects. 

2387 

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

2389 

2390 .. versionadded:: 1.4 

2391 

2392 """ 

2393 

2394 closed = False 

2395 rowcount: Optional[int] 

2396 

2397 def __init__( 

2398 self, cursor_metadata: ResultMetaData, results: Sequence[Result[_TP]] 

2399 ): 

2400 self._results = results 

2401 super().__init__( 

2402 cursor_metadata, 

2403 itertools.chain.from_iterable( 

2404 r._raw_row_iterator() for r in results 

2405 ), 

2406 ) 

2407 

2408 self._unique_filter_state = results[0]._unique_filter_state 

2409 self._yield_per = results[0]._yield_per 

2410 

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

2412 self._source_supports_scalars = results[0]._source_supports_scalars 

2413 

2414 self._attributes = self._attributes.merge_with( 

2415 *[r._attributes for r in results] 

2416 ) 

2417 

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

2419 for r in self._results: 

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

2421 if hard: 

2422 self.closed = True